def intersect(self, ray, t0=0, t1=10000): """ Given a ray p(t) = e + td and an implicit surface f(p) = 0. We can represent a sphere in vector form: (p−c)·(p−c)−R2 = 0. Any point p that satisfies this equation is on the sphere. plug in p(t) in previous equation yields: (e+td−c)·(e+td−c)−R2 = 0 which can be rearranged as : (d·d)t2 +2d·(e−c)t+(e−c)·(e−c)−R2 =0. giving us a quadratic equation, for which we can solve for t :param ray: a Ray to p(t) = e + td :return: False if the discriminant is negative True if discriminant is positive and there are two solution """ ec = ray.e - self.c A = QVector3D.dotProduct(ray.d, ray.d) B = QVector3D.dotProduct(2 * ray.d, ec) C = QVector3D.dotProduct(ec, ec) - self.r * self.r discriminant = B * B - 4 * A * C if discriminant < EPSILON: return -1.0 localt0 = (-B + math.sqrt(discriminant)) / 2 * A localt1 = (-B - math.sqrt(discriminant)) / 2 * A t = min(localt0, localt1) return min(t, t1)
def project(self, point, depth=0.0): """Returns intersection if any""" ray = self.ray(point) tMin = -math.inf tMax = math.inf obb_xform = QMatrix4x4() obb_center = QVector3D(0.0, 0.0, depth) point = obb_center - ray.origin() plane_size = [100.0, 100.0, 1E-6] for i in range(3): axis = QVector3D(obb_xform[0, i], obb_xform[1, i], obb_xform[2, i]).normalized() half_length = plane_size[i] e = QVector3D.dotProduct(axis, point) f = QVector3D.dotProduct(axis, ray.direction()) if abs(f) > 10E-6: t1 = (e + half_length) / f t2 = (e - half_length) / f if t1 > t2: w = t1 t1 = t2 t2 = w if t1 > tMin: tMin = t1 if t2 < tMax: tMax = t2 if tMin > tMax: return QVector3D(0, 0, 0) if tMax < 0: return QVector3D(0, 0, 0) elif -e - half_length > 0.0 or -e + half_length < 0.0: return QVector3D(0, 0, 0) if tMin > 0: return ray.origin() + tMin * ray.direction() return ray.origin() + tMax * ray.direction()
def intersect(self, ray): """Returns intersection if any""" tMin = -math.inf tMax = math.inf obb_xform = self.transform() obb_center = QVector3D(obb_xform[0, 3], obb_xform[1, 3], obb_xform[2, 3]) point = obb_center - ray.origin() for i in range(3): axis = QVector3D(obb_xform[0, i], obb_xform[1, i], obb_xform[2, i]).normalized() half_length = QVector3D(obb_xform[i, 0], obb_xform[i, 1], obb_xform[i, 2]).length() / 2.0 e = QVector3D.dotProduct(axis, point) f = QVector3D.dotProduct(axis, ray.direction()) if abs(f) > 10E-6: t1 = (e + half_length * self._pickFactor) / f t2 = (e - half_length * self._pickFactor) / f if t1 > t2: w = t1 t1 = t2 t2 = w if t1 > tMin: tMin = t1 if t2 < tMax: tMax = t2 if tMin > tMax: return (False, math.inf) if tMax < 0: return (False, math.inf) elif -e - half_length > 0.0 or -e + half_length < 0.0: return (False, math.inf) if tMin > 0: return (True, tMin) return (True, tMax)
def intersect(self, ray): p1 = self.vertices[0] denom = QVector3D.dotProduct(ray.d, self._normal) t = QVector3D.dotProduct(p1 - ray.e, self._normal) / denom if abs(denom) < EPSILON: # the polygon is parallel to the ray return False p = ray.e + t * ray.d return True
def move(self, x, y, width, height): self.currentPos = self.mapToSphere(x, y, width, height) self._axis = QVector3D.crossProduct(self.lastPos, self.currentPos) length = math.sqrt(QVector3D.dotProduct(self.axis, self.axis)) self.angle = QVector3D.dotProduct(self.lastPos, self.currentPos) self.lastPos = self.currentPos if length > EPSILON: return QQuaternion.fromAxisAndAngle(self._axis, self.angle) return QQuaternion.fromAxisAndAngle(0, 0, 0, 0)
def intersect(self, ray, t0=0, t1=10000): """ A point P is on the plane if Ndot(P-Q) = 0 , where Q is a point in the plane :param ray: :return: """ if QVector3D.dotProduct(self.normal, ray.d) == 0: return False t = QVector3D.dotProduct( self.normal, (self.point - self.distance) - ray.e) / QVector3D.dotProduct( self.normal, ray.d) if t < 0: return False return t
def ray_pick_test(self, origin, direction): """Check whether the given ray intersects this object. :param QVector3D origin: the camera position :param QVector3D direction: the direction vector. This MUST be normalized. :returns: the distance to the closest point of this object along the ray. Negative values if no intersection """ L = origin - QVector3D(*self.offset) t0, t1 = solve_quadratic(1, 2 * QVector3D.dotProduct(direction, L), QVector3D.dotProduct(L, L) - self.scale**2) if t0 is None or (t0 <= 0 and t1 <= 0): return -1 elif t0 > 0 and t0 < t1: return t0 else: return t1
def angleBetweenTwoVectors(A=None, B=None): ABoverABmag = QVector3D.dotProduct(A, B) / (A.length() * B.length()) if ABoverABmag > 0.999999: ABoverABmag = 1.0 elif ABoverABmag < -0.99999: ABoverABmag = -1.0 return math.degrees(math.acos(ABoverABmag))
def moveGrabber(self, dx, dy): if self.currentEdgePair is None or self.selectedGrabber is None: return cPos = self.cameraPosition() cVec = self.opts['center'] - cPos dist = cVec.length() xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) ## approx. width of view at distance of center point xScale = xDist / self.width() translationVector = QVector3D(dx, dy, 0) * xScale * 100 if translationVector.length() is not 0: self.selectedGrabber.showDirectionArrow(3) else: self.selectedGrabber.showDirectionArrow(0) transformation, res = self.projectionMatrix().inverted() translationVector = transformation * translationVector orientation = self.currentEdgePair.getOrientation() xAxis = QVector3D(orientation[0][0], orientation[0][1], orientation[0][2]) translationMag = QVector3D.dotProduct(translationVector, xAxis)/ xAxis.length() translationMag = translationMag * xScale * 200 grabberTransform = self.grabberElement.transform() grabberTransform.translate(translationMag, 0, 0) grabberPos = grabberTransform.column(3).toVector3D() max = 0 for i in range(2): edge = self.currentEdgePair[i] startPos = edge.getStartPos3D() startPos = QVector3D(startPos[0], startPos[1], startPos[2]) endPos = edge.getEndPos3D() endPos = QVector3D(endPos[0], endPos[1], endPos[2]) distV = endPos - startPos if distV.length() > max: max = distV.length() max = max/2 edgePairCenter = self.currentEdgePair.getCenterPoint3D() edgePairCenter = QVector3D(edgePairCenter[0], edgePairCenter[1], edgePairCenter[2]) difference = grabberPos - edgePairCenter if max < difference.length(): col = edgePairCenter.toVector4D() col.setW(1) grabberTransform.setColumn(3, col) if translationMag > 0: self.selectedGrabber.showDirectionArrow(1) else: max = -max self.selectedGrabber.showDirectionArrow(2) grabberTransform.translate(max, 0, 0) self.grabberElement.setTransform(grabberTransform) else: self.grabberElement.translate(translationMag, 0, 0, local=True) self.grabberElement.update() self.paintGL()
def move(self, point, quat): """Move trackball""" if not self._pressed: return currentTime = QTime.currentTime() msecs = self._lastTime.msecsTo(currentTime) if msecs <= 20: return if self._mode == self.TrackballMode.Planar: delta = QLineF(self._lastPos, point) self._angularVelocity = 180.0 * delta.length() / (math.pi * msecs) self._axis = QVector3D(-delta.dy(), delta.dx(), 0.0).normalized() self._axis = quat.rotatedVector(self._axis) self._rotation = QQuaternion.fromAxisAndAngle(self._axis, 180.0 / math.pi * delta.length()) * self._rotation elif self._mode == self.TrackballMode.Spherical: lastPos3D = QVector3D(self._lastPos.x(), self._lastPos.y(), 0.0) sqrZ = 1.0 - QVector3D.dotProduct(lastPos3D, lastPos3D) if sqrZ > 0: lastPos3D.setZ(math.sqrt(sqrZ)) else: lastPos3D.normalize() currentPos3D = QVector3D(point.x(), point.y(), 0.0) sqrZ = 1.0 - QVector3D.dotProduct(currentPos3D, currentPos3D) if sqrZ > 0: currentPos3D.setZ(math.sqrt(sqrZ)) else: currentPos3D.normalize() self._axis = QVector3D.crossProduct(lastPos3D, currentPos3D) angle = 180.0 / math.pi * math.asin(math.sqrt(QVector3D.dotProduct(self._axis, self._axis))) self._angularVelocity = angle / msecs self._axis.normalize() self._axis = quat.rotatedVector(self._axis) self._rotation = QQuaternion.fromAxisAndAngle(self._axis, angle) * self._rotation self._lastPos = point self._lastTime = currentTime
def mapToSphere(self, x, y, width, height): x, y, z = Camera.devicePortCoordinates(x, y, width, height) length = QVector3D.dotProduct(QVector3D(x, y, 0), QVector3D(x, y, 0)) z = 0 if length <= 1.0: z = math.sqrt(1.0 - length) # 1.0 is the radius of the ball pos = QVector3D(-x, y, z) else: pos = QVector3D(-x, y, z).normalized() return pos
def angle_between(v1, v2): """ Returns angle in radians between vector v1 and vector v2. @param v1 Vector 1 of class QVector3D @param v2 Vector 2 of class QVector3D """ return math.acos(QVector3D.dotProduct(v1, v2) / (v1.length() * v2.length()))
def reflect(I, N): """ Calculates reflection direction using: I - 2.0 * dot(N, I) * N. :param I: QVector3D incident vector :param N: QVector3D normal vector :return: QVector3D vector reflection direction """ Nnorm = N.normalize() R = I - 2.0 * QVector3D.dotProduct(I, N) * N return R.normalized()
def dielectric(self, obj_hit, ray, t): p = ray.e + ray.d * t n = obj_hit.material.n normal = obj_hit.normal_at(p) reflection_refraction_ray_direction = reflect(ray.d, normal) if QVector3D.dotProduct(ray.d, normal) < 0: refraction_ray_dir = refract(ray.d, normal, n)[1] cos_theta = QVector3D.dotProduct(-ray.d, normal) kr = kg = kb = 255 else: a = 1.57 kr = math.exp(-math.log(a) * t) kg = math.exp(-math.log(a) * t) kb = math.exp(-math.log(a) * t) is_ray_refracted, refraction_ray_dir = refract( ray.d, -normal, n / 0.5) if is_ray_refracted: cos_theta = QVector3D.dotProduct(refraction_ray_dir, normal) else: reflection_color = QVector3D(kr, kg, kb) color = self.hit(Ray(p, reflection_refraction_ray_direction), 10000)[1] return reflection_color * color.color R0 = ((n - 1) * (n - 1)) / ((n + 1) * (n + 1)) R = R0 + (1 - R0) * (1 - cos_theta)**5 reflection_object = self.hit( Ray(p, reflection_refraction_ray_direction), 10000)[1] reflection_object2 = self.hit(Ray(p, refraction_ray_dir), 10000)[1] if reflection_object and reflection_object2: return R * reflection_object.shader.color + ( 1 - R) * reflection_object2.shader.color + obj_hit.shader.color if reflection_object and not reflection_object2: return R * reflection_object.shader.color + obj_hit.shader.color if reflection_object2 and not reflection_object: return (1 - R) * reflection_object2.shader.color + obj_hit.shader.color
def compute(self, point, object, light, camera): """ Compute light for matte shader :param point: QVector3D point at point t :param object: Primitive object that got hit :param light: Light :param camera: Camera :return: QVector3D with calculated value for lambert """ diffuse_Color = QVector3D(0.5, 0.5, 0.5) diffuse_coefficient = 0.9 light_dir = light.direction(point) # L normal = object.normal_at(point) # N NdotL = QVector3D.dotProduct(light_dir, normal) lambert = diffuse_coefficient * diffuse_Color * light.color * max(NdotL, 0.0) return lambert
def compute(self, point, object, light, camera): """ Compute light for matte shader :param point: QVector3D point at point t :param object: Primitive object that got hit :param light: Light :param camera: Camera :return: QVector3D with calculated value for lambert """ specular_color = QVector3D(1.0, 1.0, 1.0) light_dir = light.direction(point) # L normal = object.normal_at(point) # N eye_dir = (camera.position - point).normalized() # V half_vector = (eye_dir + light_dir).normalized() NdotH = QVector3D.dotProduct(normal, half_vector) specular = light.color * specular_color * pow(max(NdotH, 0.0), light.shininess) return specular
def compute(self, point, object, light, camera): """ Compute light for Blinn-Phong shader :param point: QVector3D point at point t :param object: Primitive object that got hit :param light: Light :param camera: Camera :return: QVector3D with calculated value for lambert + specular """ specular_color = QVector3D(1.0, 1.0, 1.0) ambient_color = QVector3D(0.2,0.2,0.2) light_dir = light.direction(point) # L normal = object.normal_at(point) # N eye_dir = (camera.position - point).normalized() # V R = reflect(light_dir, normal) # R NdotR = QVector3D.dotProduct(-R, normal) specular = light.color * specular_color * pow(max(NdotR, 0.0), light.shininess) return Matte(self.color).compute(point, object, light, camera) + specular
def refract(d, n, new_index_of_refraction): """ :param d: eye dir :param n: normal :param new_index_of_refraction: :return: """ original_index_of_refraction = 1.0 # air refractive_index = original_index_of_refraction / new_index_of_refraction DdotN = QVector3D.dotProduct(d, n) inside_phi = 1 - (refractive_index * refractive_index * (1 - (DdotN * DdotN))) t = None is_ray_refracted = False # if negative there's no ray, all of the energy is reflected. known as total internal reflection if inside_phi > 0.0: cos_phi = math.sqrt(inside_phi) t = refractive_index * (d - n * DdotN) - n * cos_phi is_ray_refracted = True return is_ray_refracted, t
def __init__(self): self.center = QVector3D(0.5, 0.5, 0.5) self.eye = QVector3D(0, 0, 3) lookDirection = (self.center - self.eye).normalized() self.up = QVector3D(0, 0, 1) self.up = (self.up - lookDirection * QVector3D.dotProduct(lookDirection, self.up)).normalized() self.clearColor = [0.3, 0.25, 0.35, 1.0] self.selectedLandscapeCell = (float("inf"), float("inf")) self.lightSourcePosition = QVector3D(1.5, 1.5, 2.7) self.lastMousePosition = QPoint() try: self.n, self.m, self.landscapeHeightsMatrix = self.loadLandscapeHeightsMatrix() except: self.n, self.m = 10, 10 self.landscapeHeightsMatrix = self.generateLandscapeHeightsMatrix() self.generateWaterHeightsMatrix()
def _ortho_look_at_custom(eye_x: float, eye_y: float, eye_z: float, center_x: float, center_y: float, center_z: float, up_x: float, up_y: float, up_z: float): """ Return the ortho projection matrix :param eye_x: :param eye_y: :param eye_z: :param center_x: :param center_y: :param center_z: :param up_x: :param up_y: :param up_z: :return: the matrix """ eye_base = QVector3D(eye_x, eye_y, eye_z) up = QVector3D(up_x, up_y, up_z) center = QVector3D(center_x, center_y, center_y) eye = eye_base - center right = QVector3D.crossProduct(up, eye) up = QVector3D.crossProduct(eye, right) z_axis = QVector3D(0, 0, 1) r, theta, phi = cart2spher(eye_x, eye_y, eye_z) angle = math.pi / 2 - theta angle2 = phi + math.pi base_up = QVector3D(*spher2cart(r, angle, angle2)) # print("up", up) # print("base_up",base_up) dot_up_base_up = QVector3D.dotProduct(up, base_up) if math.isclose(dot_up_base_up, 0): angle_up_base_up = 0 else: cos_up_base_up = round(dot_up_base_up / base_up.length() / up.length(), 5) # print("cos_up_base_up",cos_up_base_up) cross_up_base_up = QVector3D.crossProduct(up, base_up) if QVector3D.dotProduct(cross_up_base_up, eye) < 0: angle_up_base_up = math.acos(cos_up_base_up) else: angle_up_base_up = math.pi * 2 - math.acos(cos_up_base_up) cos_x = math.cos(angle_up_base_up) sin_x = math.sin(angle_up_base_up) m_x = QMatrix4x4(1, 0, 0, 0, 0, cos_x, -sin_x, 0, 0, sin_x, cos_x, 0, 0, 0, 0, 1) # print("angle",math.degrees(angle_up_base_up)) # print(m_x) # 求视线矢量到z轴的投影 eye_project_z_factor = QVector3D.dotProduct( eye, z_axis) / z_axis.lengthSquared() eye_project_z = z_axis * eye_project_z_factor # 视线矢量在xy平面上的投影 eye_project_xy = eye - eye_project_z # 求视线矢量与xy平面的夹角 cos_eye_and_xy = QVector3D.dotProduct( eye, eye_project_xy) / eye_project_xy.length() / eye.length() angle_eye_and_xy = math.acos(cos_eye_and_xy) if eye.z() < 0: angle_eye_and_xy = -angle_eye_and_xy # 求 视线矢量在xy平面上的投影 与 x轴夹角 # x_axis = QVector3D(1, 0, 0) # cos_project_xy_and_x = QVector3D.dotProduct(x_axis, eye_project_xy) / x_axis.length() / eye_project_xy.length() # angle_project_xy_and_x = math.acos(cos_project_xy_and_x) # if eye_project_xy.y() < 0: # angle_project_xy_and_x = 2 * math.pi - angle_project_xy_and_x angle_project_xy_and_x = phi # print("eye", eye) # print("eye_project_z", eye_project_z) # print("eye_project_xy", eye_project_xy) # print(angle_eye_and_xy) # print(angle_project_xy_and_x) # 绕y轴逆时针旋转 angle_eye_and_xy 度 cos_1 = math.cos(angle_eye_and_xy) sin_1 = math.sin(angle_eye_and_xy) m_y = QMatrix4x4(cos_1, 0, sin_1, 0, 0, 1, 0, 0, sin_1, 0, cos_1, 0, 0, 0, 0, 1) # 绕z轴顺时针转 angle_project_xy_and_x 度 cos_2 = math.cos(-angle_project_xy_and_x) sin_2 = math.sin(-angle_project_xy_and_x) m_z = QMatrix4x4(-cos_2, sin_2, 0, 0, sin_2, cos_2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) m_ortho = QMatrix4x4(0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1) return m_ortho * m_x.transposed() * m_y.transposed() * m_z.transposed()