bool Sphere::Intersect( Ray const &objSpaceRay, float *tHit ) const { Ray r = objSpaceRay; Scalar a = Dot3( r.m_direction, r.m_direction ); Scalar b = 2.0f * Dot3( r.m_direction, r.m_origin ); Scalar c = Dot3( r.m_origin, r.m_origin ) - m_radius * m_radius; Scalar t0, t1; if( !Quadratic( a, b, c, &t0, &t1 ) ) { // ray does not intersect sphere return false; } if( t0 > r.m_maxt || t1 < r.m_mint ) { // intersection is outside of ray range return false; } Scalar t = t0; if( t0 < r.m_mint ) { // t0 is out of range, try t1 t = t1; if( t > r.m_maxt ) { // t1 is out of range return false; } } *tHit = t; return true; }
Point3 Projection3 (Point3 point, Vector3 direction, Vector3 normal) { double scalar = Dot3 (direction, normal); // Compute scalar value used in matrix double vectorScalar; // Vector scalar Point3 projection; // Point of projection to be returned Vector3 basis [3], vectorCast; // A matrix, and a vector to cast point to vectorCast = CastToVector3 (normal.tail); // Cast point to vector vectorScalar = Dot3 (vectorCast, normal) / scalar; // Compute vector scalar basis->x = (float) (1.0 - (direction.x * normal.x) / scalar); // Row 1, Column 1 of matrix (basis + 1)->x = (float) ((direction.x * normal.y) / scalar); // Row 1, Column 2 of matrix (basis + 2)->x = (float) ((direction.x * normal.z) / scalar); // Row 1, Column 3 of matrix basis->y = (float) ((direction.y * normal.x) / scalar); // Row 2, Column 1 of matrix (basis + 1)->y = (float) (1.0 - (direction.y * normal.y) / scalar); // Row 2, Column 2 of matrix (basis + 2)->y = (float) ((direction.y * normal.z) / scalar); // Row 2, Column 3 of matrix basis->z = (float) ((direction.z * normal.x) / scalar); // Row 3, Column 1 of matrix (basis + 1)->z = (float) ((direction.z * normal.y) / scalar); // Row 3, Column 2 of matrix (basis + 2)->z = (float) (1.0 - (direction.z * normal.z) / scalar); // Row 3, Column 3 of matrix Map3 (basis, &point, NULL); // Map pointCopy into a new space projection.x = (float) (point.x + vectorScalar * direction.x); // Compute the x coordinate of projection projection.y = (float) (point.y + vectorScalar * direction.y); // Compute the y coordinate of projection projection.z = (float) (point.z + vectorScalar * direction.z); // Compute the z coordinate of projection return projection; // Return point of projection }
void Matrix4x4::LookAt(const Vector4 & vFrom, const Vector4 & vTo, const Vector4 & vUp) { Vector4 vZ = Normalise(vFrom - vTo); Vector4 vX = Normalise(Cross(vUp, vZ)); Vector4 vY = Cross(vZ, vX); elem[0][0] = vX.x; elem[0][1] = vY.x; elem[0][2] = vZ.x; elem[0][3] = 0; elem[1][0] = vX.y; elem[1][1] = vY.y; elem[1][2] = vZ.y; elem[1][3] = 0; elem[2][0] = vX.z; elem[2][1] = vY.z; elem[2][2] = vZ.z; elem[2][3] = 0; elem[3][0] = Dot3(-vX, vFrom); elem[3][1] = Dot3(-vY, vFrom); elem[3][2] = Dot3(-vZ, vFrom); elem[3][3] = 1; }
double Angle3 (Vector3 vector1, Vector3 vector2, AngleMode mode) { double angle = acos (Dot3 (vector1, vector2) / (Mag3 (vector1) * Mag3 (vector2))); // Angle in given mode between two vectors switch (mode) // Get the angle mode { case kDegrees: // Degrees case angle = RADTODEG(angle); // Convert angle to radians break; // Break out of switch statement case kGradians: // Gradians case angle = RADTOGRAD(angle); // Convert angle to radians break; // Break out of switch statement case kRadians: // Radians case break; // Break out of switch statement default: NOBREAK_MESSAGE("Unsupported mode: Angle3 failed","1"); angle = 0.0;// Invalid case break; // Break out of switch statement } return angle; // Return the angle between vectors in radians }
/** * Generates a pseudo-random unit vector in the Z > 0 hemisphere, * Whose PDF == (SpecularPower + 1) / (2.0f * PI) * cos(Alpha) ^ SpecularPower in solid angles, * Where Alpha is the angle between the perfect specular direction and the outgoing direction. */ FVector4 GetModifiedPhongSpecularVector(FLMRandomStream& RandomStream, const FVector4& TangentSpecularDirection, float SpecularPower) { checkSlow(TangentSpecularDirection.Z >= 0.0f); checkSlow(SpecularPower > 0.0f); FVector4 GeneratedTangentVector; do { // Generate hemispherical coordinates in the local frame of the perfect specular direction // Don't allow a value of 0, since that results in a PDF of 0 with large specular powers due to floating point imprecision const float Alpha = FMath::Min(FMath::Acos(FMath::Pow(FMath::Max(RandomStream.GetFraction(), DELTA), 1.0f / (SpecularPower + 1.0f))), (float)HALF_PI - DELTA); const float Phi = 2.0f * (float)PI * RandomStream.GetFraction(); // Convert to Cartesian, still in the coordinate space of the perfect specular direction const float SinTheta = FMath::Sin(Alpha); const FVector4 GeneratedSpecularTangentVector(FMath::Cos(Phi) * SinTheta, FMath::Sin(Phi) * SinTheta, FMath::Cos(Alpha)); // Generate the X and Y axes of the coordinate space whose Z is the perfect specular direction FVector4 SpecularTangentX = (TangentSpecularDirection ^ FVector4(0,1,0)).GetUnsafeNormal3(); if (SpecularTangentX.SizeSquared3() < KINDA_SMALL_NUMBER) { // The specular direction was nearly equal to the Y axis, use the X axis instead SpecularTangentX = (TangentSpecularDirection ^ FVector4(1,0,0)).GetUnsafeNormal3(); } else { SpecularTangentX = SpecularTangentX.GetUnsafeNormal3(); } const FVector4 SpecularTangentY = TangentSpecularDirection ^ SpecularTangentX; // Rotate the generated coordinates into the local frame of the tangent space normal (0,0,1) const FVector4 SpecularTangentRow0(SpecularTangentX.X, SpecularTangentY.X, TangentSpecularDirection.X); const FVector4 SpecularTangentRow1(SpecularTangentX.Y, SpecularTangentY.Y, TangentSpecularDirection.Y); const FVector4 SpecularTangentRow2(SpecularTangentX.Z, SpecularTangentY.Z, TangentSpecularDirection.Z); GeneratedTangentVector = FVector4( Dot3(SpecularTangentRow0, GeneratedSpecularTangentVector), Dot3(SpecularTangentRow1, GeneratedSpecularTangentVector), Dot3(SpecularTangentRow2, GeneratedSpecularTangentVector) ); } // Regenerate an Alpha as long as the direction is outside of the tangent space Z > 0 hemisphere, // Since some part of the cosine lobe around the specular direction can be outside of the hemisphere around the surface normal. while (GeneratedTangentVector.Z < DELTA); return GeneratedTangentVector; }
/** Generates valid X and Y axes of a coordinate system, given the Z axis. */ void GenerateCoordinateSystem2(const FVector4& ZAxis, FVector4& XAxis, FVector4& YAxis) { // This implementation is based off of the one from 'Physically Based Rendering' if (FMath::Abs(ZAxis.X) > FMath::Abs(ZAxis.Y)) { const float InverseLength = FMath::InvSqrt(ZAxis.X * ZAxis.X + ZAxis.Z * ZAxis.Z); XAxis = FVector4(-ZAxis.Z * InverseLength, 0.0f, ZAxis.X * InverseLength); } else { const float InverseLength = FMath::InvSqrt(ZAxis.Y * ZAxis.Y + ZAxis.Z * ZAxis.Z); XAxis = FVector4(0.0f, ZAxis.Z * InverseLength, -ZAxis.Y * InverseLength); } YAxis = ZAxis ^ XAxis; checkSlow(YAxis.IsUnit3()); checkSlow(FMath::Abs(Dot3(XAxis, ZAxis)) <= THRESH_NORMALS_ARE_ORTHOGONAL); checkSlow(FMath::Abs(Dot3(YAxis, ZAxis)) <= THRESH_NORMALS_ARE_ORTHOGONAL); checkSlow(FMath::Abs(Dot3(XAxis, YAxis)) <= THRESH_NORMALS_ARE_ORTHOGONAL); }
bool GetBarycentricWeights( const FVector4& Position0, const FVector4& Position1, const FVector4& Position2, const FVector4& InterpolatePosition, float Tolerance, FVector4& BarycentricWeights ) { BarycentricWeights = FVector4(0,0,0); FVector4 TriangleNormal = (Position0 - Position1) ^ (Position2 - Position0); float ParallelogramArea = TriangleNormal.Size3(); FVector4 UnitTriangleNormal = TriangleNormal / ParallelogramArea; float PlaneDistance = Dot3(UnitTriangleNormal, (InterpolatePosition - Position0)); // Only continue if the position to interpolate to is in the plane of the triangle (within some error) if (FMath::Abs(PlaneDistance) < Tolerance) { // Move the position to interpolate to into the plane of the triangle along the normal, // Otherwise there will be error in our barycentric coordinates FVector4 AdjustedInterpolatePosition = InterpolatePosition - UnitTriangleNormal * PlaneDistance; FVector4 NormalU = (AdjustedInterpolatePosition - Position1) ^ (Position2 - AdjustedInterpolatePosition); // Signed area, if negative then InterpolatePosition is not in the triangle float ParallelogramAreaU = NormalU.Size3() * (Dot3(NormalU, TriangleNormal) > 0.0f ? 1.0f : -1.0f); float BaryCentricU = ParallelogramAreaU / ParallelogramArea; FVector4 NormalV = (AdjustedInterpolatePosition - Position2) ^ (Position0 - AdjustedInterpolatePosition); float ParallelogramAreaV = NormalV.Size3() * (Dot3(NormalV, TriangleNormal) > 0.0f ? 1.0f : -1.0f); float BaryCentricV = ParallelogramAreaV / ParallelogramArea; float BaryCentricW = 1.0f - BaryCentricU - BaryCentricV; if (BaryCentricU > -Tolerance && BaryCentricV > -Tolerance && BaryCentricW > -Tolerance) { BarycentricWeights = FVector4(BaryCentricU, BaryCentricV, BaryCentricW); return true; } } return false; }
float ComputeBoundsScreenSize( const FVector4& Origin, const float SphereRadius, const FSceneView& View ) { // Only need one component from a view transformation; just calculate the one we're interested in. const float Divisor = Dot3(Origin - View.ViewMatrices.ViewOrigin, View.ViewMatrices.ViewMatrix.GetColumn(2)); // Get projection multiple accounting for view scaling. const float ScreenMultiple = FMath::Max(View.ViewRect.Width() / 2.0f * View.ViewMatrices.ProjMatrix.M[0][0], View.ViewRect.Height() / 2.0f * View.ViewMatrices.ProjMatrix.M[1][1]); const float ScreenRadius = ScreenMultiple * SphereRadius / FMath::Max(Divisor, 1.0f); const float ScreenArea = PI * ScreenRadius * ScreenRadius; return FMath::Clamp(ScreenArea / View.ViewRect.Area(), 0.0f, 1.0f); }
Point3 PerspectiveProjection (Point3 point, Vector3 normal) { double scalar; // Scalar value Vector3 vectorCast; // A vector to cast point to Point3 projection; // Point of perspective projection to be returned vectorCast = CastToVector3 (normal.tail); // Cast point to vector scalar = Dot3 (vectorCast, normal); // Compute first portion of scalar value vectorCast = CastToVector3 (point); // Cast point to vector scalar /= Dot3 (vectorCast, normal); // Compute second portion of scalar value projection.x = (float) (scalar * point.x); // Compute x coordinate of projection projection.y = (float) (scalar * point.y); // Compute y coordinate of projection projection.z = (float) (scalar * point.z); // Compute z coordinate of projection return projection; // Return point of perspective projection }
void MtlBlinnSW3D::ShadeFragment(ShadeContext3D &sc) { BlinnBlock &block = (BlinnBlock &) sc.GetMaterialBlock(this); // diffuse color const Vector4f &vertexColor = IsSet(block.VertexColor) ? *block.VertexColor : WHITE4F; Color4f diffuseMtl; if (DiffuseMtl) { DiffuseMtl->Shade(sc); diffuseMtl = *block.Color; } else diffuseMtl = WHITE4F; Color4f Kd = Mul(vertexColor, Mul(Diffuse, diffuseMtl)); if (!sc.DoLighting || (!ReceivesLighting && !ReceivesShadows)) *block.Color = Kd; else { Vector4f oldNormal; Color4f diffuseMtl; // position Vector4f p = Normalize3(*block.EyePosition); // normal if (BumpmapMtl) { oldNormal = *block.EyeNormal; // save normal BumpmapMtl->Shade(sc); } Vector4f n = Normalize3(*block.EyeNormal); if (BumpmapMtl) *block.EyeNormal = oldNormal; // restore normal if (UseTwoSidedLighting && Dot3(n, p) > 0.0f) n = -n; // specular Color4f specularMtl; float32 specularIntensityMtl, specularExponentMtl; if (SpecularMtl) { SpecularMtl->Shade(sc); specularMtl = *block.Color; } else specularMtl = WHITE4F; if (SpecularIntensityMtl) { SpecularIntensityMtl->Shade(sc); specularIntensityMtl = block.Color->A; } else specularIntensityMtl = 1.0f; if (SpecularExponentMtl) { SpecularExponentMtl->Shade(sc); specularExponentMtl = block.Color->A; } else specularExponentMtl = 1.0f; Color4f Ks = (SpecularIntensity * specularIntensityMtl) * Mul(vertexColor, Mul(Specular, specularMtl)); float32 specularExponent = SpecularExponent * specularExponentMtl; // do blinn lighting Color4f ambientAccum = ZERO4F; Color4f diffuseAccum = ZERO4F; Color4f specularAccum = ZERO4F; //uassert(sc.X != 742 || sc.Y != 320); const int nLights = sc.NumLights; for (int i = 0; i < nLights; i++) { if (sc.Illuminate(i)) { if (ReceivesLighting) { if (sc.CastsShadows(i) && ReceivesShadows) { // get the transmittance (incl shadowdensity) and transmitted light and use them to apply shadows sc.Shadow(i); sc.LightColor = Lerp(sc.LightColor, sc.TransmittedLight, sc.Transmittance.A); } const Vector4f &l = (const Vector4f &) sc.LightVector; ambientAccum += sc.AmbientLightColor; if (sc.DoDiffuse) { float32 diffuse = maxf(Dot3(n, l), 0.0f); diffuseAccum += diffuse * Mul(Kd, sc.LightColor); } if (sc.DoSpecular) { Vector4f h = Normalize3(l - p); // half angle vector float32 specular = powf(maxf(Dot3(n, h), 0.0f), specularExponent); specularAccum += specular * sc.LightColor; } } else { if (sc.CastsShadows(i) && ReceivesShadows) { sc.Shadow(i); diffuseAccum += Lerp(Kd, Mul(sc.Transmittance, Kd), sc.Transmittance.A); } } } } if (!sc.DoAmbient) ambientAccum = ZERO4F; // combine diffuse/specular/ambient Color4f outColor = Mul(ambientAccum, Kd) + diffuseAccum + Mul(specularAccum, Ks); outColor.A = Kd.A; FuASSERT(!outColor.IsNaN(), ("MtlBlinnSW3D::NaN output")); *block.Color = outColor; } }
bool Vector::OrthogonalTo3(const Vector& other) const { return fabs(Dot3(other)) < VEC_EPS; }
/** Generates a single z layer of VolumeDistanceField. */ void FStaticLightingSystem::CalculateVolumeDistanceFieldWorkRange(int32 ZIndex) { const double StartTime = FPlatformTime::Seconds(); FStaticLightingMappingContext MappingContext(NULL, *this); TArray<FVector4> SampleDirections; TArray<FVector2D> SampleDirectionUniforms; const int32 NumThetaSteps = FMath::Trunc(FMath::Sqrt(VolumeDistanceFieldSettings.NumVoxelDistanceSamples / (2.0f * (float)PI))); const int32 NumPhiSteps = FMath::Trunc(NumThetaSteps * (float)PI); FLMRandomStream RandomStream(0); GenerateStratifiedUniformHemisphereSamples(NumThetaSteps, NumPhiSteps, RandomStream, SampleDirections, SampleDirectionUniforms); TArray<FVector4> OtherHemisphereSamples; TArray<FVector2D> OtherSampleDirectionUniforms; GenerateStratifiedUniformHemisphereSamples(NumThetaSteps, NumPhiSteps, RandomStream, OtherHemisphereSamples, OtherSampleDirectionUniforms); for (int32 i = 0; i < OtherHemisphereSamples.Num(); i++) { FVector4 Sample = OtherHemisphereSamples[i]; Sample.Z *= -1; SampleDirections.Add(Sample); } const FVector4 CellExtents = FVector4(DistanceFieldVoxelSize / 2, DistanceFieldVoxelSize / 2, DistanceFieldVoxelSize / 2); for (int32 YIndex = 0; YIndex < VolumeSizeY; YIndex++) { for (int32 XIndex = 0; XIndex < VolumeSizeX; XIndex++) { const FVector4 VoxelPosition = FVector4(XIndex, YIndex, ZIndex) * DistanceFieldVoxelSize + DistanceFieldVolumeBounds.Min + CellExtents; const int32 Index = ZIndex * VolumeSizeY * VolumeSizeX + YIndex * VolumeSizeX + XIndex; float MinDistance[2]; MinDistance[0] = FLT_MAX; MinDistance[1] = FLT_MAX; int32 Hit[2]; Hit[0] = 0; Hit[1] = 0; int32 HitFront[2]; HitFront[0] = 0; HitFront[1] = 0; // Generate two distance fields // The first is for mostly horizontal triangles, the second is for mostly vertical triangles // Keeping them separate allows reconstructing a cleaner surface, // Otherwise there would be holes in the surface where an unclosed wall mesh intersects an unclosed ground mesh for (int32 i = 0; i < 2; i++) { for (int32 SampleIndex = 0; SampleIndex < SampleDirections.Num(); SampleIndex++) { const float ExtentDistance = DistanceFieldVolumeBounds.GetExtent().GetMax() * 2.0f; FLightRay Ray( VoxelPosition, VoxelPosition + SampleDirections[SampleIndex] * VolumeDistanceFieldSettings.VolumeMaxDistance, NULL, NULL ); // Trace rays in all directions to find the closest solid surface FLightRayIntersection Intersection; AggregateMesh.IntersectLightRay(Ray, true, false, false, MappingContext.RayCache, Intersection); if (Intersection.bIntersects) { if ((i == 0 && FMath::Abs(Intersection.IntersectionVertex.WorldTangentZ.Z) >= .707f || i == 1 && FMath::Abs(Intersection.IntersectionVertex.WorldTangentZ.Z) < .707f)) { Hit[i]++; if (Dot3(Ray.Direction, Intersection.IntersectionVertex.WorldTangentZ) < 0) { HitFront[i]++; } const float CurrentDistance = (VoxelPosition - Intersection.IntersectionVertex.WorldPosition).Size3(); if (CurrentDistance < MinDistance[i]) { MinDistance[i] = CurrentDistance; } } } } // Consider this voxel 'outside' an object if more than 75% of the rays hit front faces MinDistance[i] *= (Hit[i] == 0 || HitFront[i] > Hit[i] * .75f) ? 1 : -1; } // Create a mask storing where an intersection can possibly take place // This allows the reconstruction to ignore areas where large positive and negative distances come together, // Which is caused by unclosed surfaces. const uint8 Mask0 = FMath::Abs(MinDistance[0]) < DistanceFieldVoxelSize * 2 ? 255 : 0; // 0 will be -MaxDistance, .5 will be 0, 1 will be +MaxDistance const float NormalizedDistance0 = FMath::Clamp(MinDistance[0] / VolumeDistanceFieldSettings.VolumeMaxDistance + .5f, 0.0f, 1.0f); const uint8 Mask1 = FMath::Abs(MinDistance[1]) < DistanceFieldVoxelSize * 2 ? 255 : 0; const float NormalizedDistance1 = FMath::Clamp(MinDistance[1] / VolumeDistanceFieldSettings.VolumeMaxDistance + .5f, 0.0f, 1.0f); const FColor FinalValue( FMath::Clamp<uint8>(FMath::Trunc(NormalizedDistance0 * 255), 0, 255), FMath::Clamp<uint8>(FMath::Trunc(NormalizedDistance1 * 255), 0, 255), Mask0, Mask1 ); VolumeDistanceField[Index] = FinalValue; } } MappingContext.Stats.VolumeDistanceFieldThreadTime = FPlatformTime::Seconds() - StartTime; }