public void InitializeCamera(float3 _position, float3 _target, float3 _up) { // Build the camera matrix float3 At = _target - _position; if (At.LengthSquared > 1e-2f) { // Normal case m_CameraTargetDistance = At.Length; At /= m_CameraTargetDistance; } else { // Special bad case m_CameraTargetDistance = 0.01f; At = new float3(0.0f, 0.0f, -1.0f); } float3 ortho = _up.Cross(At).Normalized; float4x4 cameraMat = float4x4.Identity; cameraMat[3] = new float4(_position, 1.0f); cameraMat[2] = new float4(At, 0.0f); cameraMat[0] = new float4(ortho, 0.0f); cameraMat[1] = new float4(At.Cross(ortho), 0.0f); CameraTransform = cameraMat; // Setup the normalized target distance m_NormalizedTargetDistance = NormalizeTargetDistance(m_CameraTargetDistance); }
public void InitializeCamera(float3 _Position, float3 _Target, float3 _Up) { // Build the camera matrix float3 At = _Target - _Position; if (At.LengthSquared > 1e-2f) { // Normal case m_CameraTargetDistance = At.Length; At /= m_CameraTargetDistance; } else { // Special bad case m_CameraTargetDistance = 0.01f; At = new float3(0.0f, 0.0f, -1.0f); } float3 Ortho = _Up.Cross(At).Normalized; float4x4 CameraMat = float4x4.Identity; CameraMat.r3 = new float4(_Position, 1.0f); CameraMat.r2 = new float4(At, 0.0f); CameraMat.r0 = new float4(Ortho, 0.0f); CameraMat.r1 = new float4(At.Cross(Ortho), 0.0f); CameraTransform = CameraMat; // Setup the normalized target distance m_NormalizedTargetDistance = NormalizeTargetDistance(m_CameraTargetDistance); }
/// <summary> /// Initializes the collection of samples /// </summary> /// <param name="_Order">The order of the SH</param> /// <param name="_ThetaSamplesCount">The amount of samples on Theta (total samples count will be 2*N*N)</param> /// <param name="_Up">The vector to use as the Up direction (use [0,1,0] if not sure)</param> public void Initialize(int _Order, int _ThetaSamplesCount, float3 _Up) { m_Order = _Order; m_ThetaSamplesCount = _ThetaSamplesCount; m_SamplesCount = 2 * m_ThetaSamplesCount * m_ThetaSamplesCount; m_Random = new Random(m_RandomSeed); // Compute the rotation matrix float3x3 Rotation = new float3x3(); float3 Ortho = float3.UnitY.Cross(_Up); float fNorm = Ortho.LengthSquared; if (fNorm < 1e-6f) { Rotation = float3x3.Identity; } else { Ortho /= (float)Math.Sqrt(fNorm); Rotation.r0 = Ortho; Rotation.r1 = _Up; Rotation.r2 = Ortho.Cross(_Up); } // Initialize the SH samples m_SHSamples = new SHSample[m_SamplesCount]; // Build the samples using stratified sampling int SampleIndex = 0; for (int ThetaIndex = 0; ThetaIndex < m_ThetaSamplesCount; ThetaIndex++) { for (int PhiIndex = 0; PhiIndex < 2 * m_ThetaSamplesCount; PhiIndex++) { double fTheta = 2.0 * System.Math.Acos(System.Math.Sqrt(1.0 - (ThetaIndex + m_Random.NextDouble()) / m_ThetaSamplesCount)); double fPhi = System.Math.PI * (PhiIndex + m_Random.NextDouble()) / m_ThetaSamplesCount; // Compute direction, rotate it then cast it back to sphercial coordinates float3 Direction = SphericalHarmonics.SHFunctions.SphericalToCartesian(fTheta, fPhi); Direction *= Rotation; SphericalHarmonics.SHFunctions.CartesianToSpherical(Direction, out fTheta, out fPhi); // Fill up the new sample m_SHSamples[SampleIndex] = new SHSample((float)fPhi, (float)fTheta); m_SHSamples[SampleIndex].m_SHFactors = new double[m_Order * m_Order]; // Build the SH Factors SHFunctions.InitializeSHCoefficients(m_Order, fTheta, fPhi, m_SHSamples[SampleIndex].m_SHFactors); SampleIndex++; } } }
void DrawArrow(float3 _start, float3 _end, float3 _ortho, float4 _color) { DrawLine(_start, _end, _ortho, -1.0f, _color); float3 dir = _end - _start; float size = dir.Length; dir /= size; size *= 0.1f; DrawPlane(_end - 0.5f * size * dir, _ortho, dir.Cross(_ortho), 0.0f, 0.5f * size, size, _color, false); }
public CellPolygon(float3 _P, float3 _N) { m_P = _P; m_N = _N; m_T = (float3.UnitY.Cross(m_N)).Normalized; m_B = m_N.Cross(m_T); // Start with 4 vertices const float R = 10.0f; m_Vertices = new float3[] { m_P + R * (-m_T + m_B), m_P + R * (-m_T - m_B), m_P + R * (m_T - m_B), m_P + R * (m_T + m_B), }; }
/// <summary> /// Build the cube primitive /// </summary> void BuildCube() { float3[] Normals = new float3[6] { -float3.UnitX, float3.UnitX, -float3.UnitY, float3.UnitY, -float3.UnitZ, float3.UnitZ, }; float3[] Tangents = new float3[6] { float3.UnitZ, -float3.UnitZ, float3.UnitX, -float3.UnitX, -float3.UnitX, float3.UnitX, }; VertexP3N3[] vertices = new VertexP3N3[6 * 4]; uint[] indices = new uint[2 * 6 * 3]; for (int FaceIndex = 0; FaceIndex < 6; FaceIndex++) { float3 N = Normals[FaceIndex]; float3 T = Tangents[FaceIndex]; float3 B = N.Cross(T); vertices[4 * FaceIndex + 0] = new VertexP3N3() { P = N - T + B, N = N, // T = T, // B = B, // UV = new float2( 0, 0 ) }; vertices[4 * FaceIndex + 1] = new VertexP3N3() { P = N - T - B, N = N, // T = T, // B = B, // UV = new float2( 0, 1 ) }; vertices[4 * FaceIndex + 2] = new VertexP3N3() { P = N + T - B, N = N, // T = T, // B = B, // UV = new float2( 1, 1 ) }; vertices[4 * FaceIndex + 3] = new VertexP3N3() { P = N + T + B, N = N, // T = T, // B = B, // UV = new float2( 1, 0 ) }; indices[2 * 3 * FaceIndex + 0] = (uint)(4 * FaceIndex + 0); indices[2 * 3 * FaceIndex + 1] = (uint)(4 * FaceIndex + 1); indices[2 * 3 * FaceIndex + 2] = (uint)(4 * FaceIndex + 2); indices[2 * 3 * FaceIndex + 3] = (uint)(4 * FaceIndex + 0); indices[2 * 3 * FaceIndex + 4] = (uint)(4 * FaceIndex + 2); indices[2 * 3 * FaceIndex + 5] = (uint)(4 * FaceIndex + 3); } m_primitiveCube = new Primitive(m_device, (uint)vertices.Length, VertexP3N3.FromArray(vertices), indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3); // VertexP3N3[] vertices = new VertexP3N3[4*6]; // uint[] indices = new uint[6*2*3]; // for ( uint face=0; face < 6; face++ ) { // float3 N = (1 - 2 * (face & 1)) * new float3( (face >> 1) == 0 ? 1 : 0, (face >> 1) == 1 ? 1 : 0, (face >> 1) == 2 ? 1 : 0 ); // float3 T = (1 - 2 * (face & 1)) * new float3( (face >> 1) == 1 ? 1 : 0, (face >> 1) == 2 ? 1 : 0, (face >> 1) == 0 ? 1 : 0 ); // float3 B = T.Cross( N ); // // for ( uint v=0; v < 4; v++ ) { // vertices[4*face+v].N = N; // vertices[4*face+v].P = N + (2.0f*(v & 1)-1) * T + (2.0f*((v >> 1) & 1)-1) * B; // } // // indices[3*(2*face+0)+0] = 4*face + 0; // indices[3*(2*face+0)+1] = 4*face + 1; // indices[3*(2*face+0)+2] = 4*face + 2; // indices[3*(2*face+1)+0] = 4*face + 2; // indices[3*(2*face+1)+1] = 4*face + 1; // indices[3*(2*face+1)+2] = 4*face + 3; // } // // m_primitiveCube = new Primitive( m_device, 6*4, VertexP3N3.FromArray( vertices ), indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3 ); }
private void BuildPrimitives() { { VertexPt4[] Vertices = new VertexPt4[4]; Vertices[0] = new VertexPt4() { Pt = new float4(-1, +1, 0, 1) }; // Top-Left Vertices[1] = new VertexPt4() { Pt = new float4(-1, -1, 0, 1) }; // Bottom-Left Vertices[2] = new VertexPt4() { Pt = new float4(+1, +1, 0, 1) }; // Top-Right Vertices[3] = new VertexPt4() { Pt = new float4(+1, -1, 0, 1) }; // Bottom-Right ByteBuffer VerticesBuffer = VertexPt4.FromArray(Vertices); m_Prim_Quad = new Primitive(m_Device, Vertices.Length, VerticesBuffer, null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.Pt4); } { VertexP3N3G3B3T2[] Vertices = new VertexP3N3G3B3T2[4]; Vertices[0] = new VertexP3N3G3B3T2() { P = new float3(-1, +1, 0), N = new float3(0, 0, 1), T = new float3(1, 0, 0), B = new float3(0, 1, 0), UV = new float2(0, 0) }; // Top-Left Vertices[1] = new VertexP3N3G3B3T2() { P = new float3(-1, -1, 0), N = new float3(0, 0, 1), T = new float3(1, 0, 0), B = new float3(0, 1, 0), UV = new float2(0, 1) }; // Bottom-Left Vertices[2] = new VertexP3N3G3B3T2() { P = new float3(+1, +1, 0), N = new float3(0, 0, 1), T = new float3(1, 0, 0), B = new float3(0, 1, 0), UV = new float2(1, 0) }; // Top-Right Vertices[3] = new VertexP3N3G3B3T2() { P = new float3(+1, -1, 0), N = new float3(0, 0, 1), T = new float3(1, 0, 0), B = new float3(0, 1, 0), UV = new float2(1, 1) }; // Bottom-Right ByteBuffer VerticesBuffer = VertexP3N3G3B3T2.FromArray(Vertices); m_Prim_Rectangle = new Primitive(m_Device, Vertices.Length, VerticesBuffer, null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3B3T2); } { // Build the sphere const int W = 41; const int H = 22; VertexP3N3G3B3T2[] Vertices = new VertexP3N3G3B3T2[W * H]; for (int Y = 0; Y < H; Y++) { double Theta = Math.PI * Y / (H - 1); float CosTheta = (float)Math.Cos(Theta); float SinTheta = (float)Math.Sin(Theta); for (int X = 0; X < W; X++) { double Phi = 2.0 * Math.PI * X / (W - 1); float CosPhi = (float)Math.Cos(Phi); float SinPhi = (float)Math.Sin(Phi); float3 N = new float3(SinTheta * SinPhi, CosTheta, SinTheta * CosPhi); float3 T = new float3(CosPhi, 0.0f, -SinPhi); float3 B = N.Cross(T); Vertices[W * Y + X] = new VertexP3N3G3B3T2() { P = N, N = N, T = T, B = B, UV = new float2(2.0f * X / W, 1.0f * Y / H) }; } } ByteBuffer VerticesBuffer = VertexP3N3G3B3T2.FromArray(Vertices); uint[] Indices = new uint[(H - 1) * (2 * W + 2) - 2]; int IndexCount = 0; for (int Y = 0; Y < H - 1; Y++) { for (int X = 0; X < W; X++) { Indices[IndexCount++] = (uint)((Y + 0) * W + X); Indices[IndexCount++] = (uint)((Y + 1) * W + X); } if (Y < H - 2) { Indices[IndexCount++] = (uint)((Y + 1) * W - 1); Indices[IndexCount++] = (uint)((Y + 1) * W + 0); } } m_Prim_Sphere = new Primitive(m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3B3T2); } { // Build the cube float3[] Normals = new float3[6] { -float3.UnitX, float3.UnitX, -float3.UnitY, float3.UnitY, -float3.UnitZ, float3.UnitZ, }; float3[] Tangents = new float3[6] { float3.UnitZ, -float3.UnitZ, float3.UnitX, -float3.UnitX, -float3.UnitX, float3.UnitX, }; VertexP3N3G3B3T2[] Vertices = new VertexP3N3G3B3T2[6 * 4]; uint[] Indices = new uint[2 * 6 * 3]; for (int FaceIndex = 0; FaceIndex < 6; FaceIndex++) { float3 N = Normals[FaceIndex]; float3 T = Tangents[FaceIndex]; float3 B = N.Cross(T); Vertices[4 * FaceIndex + 0] = new VertexP3N3G3B3T2() { P = N - T + B, N = N, T = T, B = B, UV = new float2(0, 0) }; Vertices[4 * FaceIndex + 1] = new VertexP3N3G3B3T2() { P = N - T - B, N = N, T = T, B = B, UV = new float2(0, 1) }; Vertices[4 * FaceIndex + 2] = new VertexP3N3G3B3T2() { P = N + T - B, N = N, T = T, B = B, UV = new float2(1, 1) }; Vertices[4 * FaceIndex + 3] = new VertexP3N3G3B3T2() { P = N + T + B, N = N, T = T, B = B, UV = new float2(1, 0) }; Indices[2 * 3 * FaceIndex + 0] = (uint)(4 * FaceIndex + 0); Indices[2 * 3 * FaceIndex + 1] = (uint)(4 * FaceIndex + 1); Indices[2 * 3 * FaceIndex + 2] = (uint)(4 * FaceIndex + 2); Indices[2 * 3 * FaceIndex + 3] = (uint)(4 * FaceIndex + 0); Indices[2 * 3 * FaceIndex + 4] = (uint)(4 * FaceIndex + 2); Indices[2 * 3 * FaceIndex + 5] = (uint)(4 * FaceIndex + 3); } ByteBuffer VerticesBuffer = VertexP3N3G3B3T2.FromArray(Vertices); m_Prim_Cube = new Primitive(m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2); } }
void BuildPrimitives() { { // Post-process quad List<VertexPt4> Vertices = new List<VertexPt4>(); Vertices.Add( new VertexPt4() { Pt=new float4( -1, +1, 0, 1 ) } ); Vertices.Add( new VertexPt4() { Pt=new float4( -1, -1, 0, 1 ) } ); Vertices.Add( new VertexPt4() { Pt=new float4( +1, +1, 0, 1 ) } ); Vertices.Add( new VertexPt4() { Pt=new float4( +1, -1, 0, 1 ) } ); m_Prim_Quad = new Primitive( m_Device, 4, VertexPt4.FromArray( Vertices.ToArray() ), null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.Pt4 ); } { // Sphere Primitive List<VertexP3N3G3T2> Vertices = new List<VertexP3N3G3T2>(); List<uint> Indices = new List<uint>(); const int SUBDIVS_THETA = 80; const int SUBDIVS_PHI = 160; for ( int Y=0; Y <= SUBDIVS_THETA; Y++ ) { double Theta = Y * Math.PI / SUBDIVS_THETA; float CosTheta = (float) Math.Cos( Theta ); float SinTheta = (float) Math.Sin( Theta ); for ( int X=0; X <= SUBDIVS_PHI; X++ ) { double Phi = X * 2.0 * Math.PI / SUBDIVS_PHI; float CosPhi = (float) Math.Cos( Phi ); float SinPhi = (float) Math.Sin( Phi ); float3 N = new float3( SinTheta*SinPhi, CosTheta, SinTheta*CosPhi ); float3 T = new float3( CosPhi, 0.0f, -SinPhi ); float2 UV = new float2( (float) X / SUBDIVS_PHI, (float) Y / SUBDIVS_THETA ); Vertices.Add( new VertexP3N3G3T2() { P=N, N=N, T=T, UV=UV } ); } } for ( int Y=0; Y < SUBDIVS_THETA; Y++ ) { int CurrentLineOffset = Y * (SUBDIVS_PHI+1); int NextLineOffset = (Y+1) * (SUBDIVS_PHI+1); for ( int X=0; X <= SUBDIVS_PHI; X++ ) { Indices.Add( (uint) (CurrentLineOffset + X) ); Indices.Add( (uint) (NextLineOffset + X) ); } if ( Y < SUBDIVS_THETA-1 ) { Indices.Add( (uint) (NextLineOffset - 1) ); // Degenerate triangle to end the line Indices.Add( (uint) NextLineOffset ); // Degenerate triangle to start the next line } } m_Prim_Sphere = new Primitive( m_Device, Vertices.Count, VertexP3N3G3T2.FromArray( Vertices.ToArray() ), Indices.ToArray(), Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3T2 ); } { // Build the cube float3[] Normals = new float3[6] { -float3.UnitX, float3.UnitX, -float3.UnitY, float3.UnitY, -float3.UnitZ, float3.UnitZ, }; float3[] Tangents = new float3[6] { float3.UnitZ, -float3.UnitZ, float3.UnitX, -float3.UnitX, -float3.UnitX, float3.UnitX, }; VertexP3N3G3T2[] Vertices = new VertexP3N3G3T2[6*4]; uint[] Indices = new uint[2*6*3]; for ( int FaceIndex=0; FaceIndex < 6; FaceIndex++ ) { float3 N = Normals[FaceIndex]; float3 T = Tangents[FaceIndex]; float3 B = N.Cross( T ); Vertices[4*FaceIndex+0] = new VertexP3N3G3T2() { P = N - T + B, N = N, T = T, // B = B, UV = new float2( 0, 0 ) }; Vertices[4*FaceIndex+1] = new VertexP3N3G3T2() { P = N - T - B, N = N, T = T, // B = B, UV = new float2( 0, 1 ) }; Vertices[4*FaceIndex+2] = new VertexP3N3G3T2() { P = N + T - B, N = N, T = T, // B = B, UV = new float2( 1, 1 ) }; Vertices[4*FaceIndex+3] = new VertexP3N3G3T2() { P = N + T + B, N = N, T = T, // B = B, UV = new float2( 1, 0 ) }; Indices[2*3*FaceIndex+0] = (uint) (4*FaceIndex+0); Indices[2*3*FaceIndex+1] = (uint) (4*FaceIndex+1); Indices[2*3*FaceIndex+2] = (uint) (4*FaceIndex+2); Indices[2*3*FaceIndex+3] = (uint) (4*FaceIndex+0); Indices[2*3*FaceIndex+4] = (uint) (4*FaceIndex+2); Indices[2*3*FaceIndex+5] = (uint) (4*FaceIndex+3); } ByteBuffer VerticesBuffer = VertexP3N3G3T2.FromArray( Vertices ); m_Prim_Cube = new Primitive( m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3T2 ); } }
// float DiskIrradiance(float3[] _tsQuadVertices) { // 1) Extract center position, tangent and bi-tangent axes float3 T = 0.5f * (_tsQuadVertices[1] - _tsQuadVertices[2]); float3 B = -0.5f * (_tsQuadVertices[3] - _tsQuadVertices[2]); float3 P0 = 0.5f * (_tsQuadVertices[0] + _tsQuadVertices[2]); // Half-way through the diagonal float sqRt = T.Dot(T); float sqRb = B.Dot(B); float3 N = T.Cross(B).Normalized; // @TODO: Optimize! Do we need to normalize anyway? // 2) Build frustum matrices // M transform P' = M * P into canonical frustum space float3x3 M; float invDepth = 1.0f / P0.Dot(N); M.r0 = (T - P0.Dot(T) * invDepth * N) / sqRt; M.r1 = (B - P0.Dot(B) * invDepth * N) / sqRb; M.r2 = N * invDepth; // M^-1 transforms P = M^-1 * P' back into original space float3x3 invM = new float3x3(); invM.r0.Set(T.x, B.x, P0.x); invM.r1.Set(T.y, B.y, P0.y); invM.r2.Set(T.z, B.z, P0.z); // Compute the determinant of M^-1 that will help us scale the resulting vector float det = invM.Determinant; det = Mathf.Sqrt(sqRt * sqRb) * P0.Dot(N); // 3) Compute the exact integral in the canonical space // We know the expression of the orthogonal vector at any point on the unit circle as: // // | cos(theta) // Tau(theta) = 1/sqrt(2) | sin(theta) // | 1 // // We move the orientation so we always compute 2 symetrical integrals on a half circle // then we only need to compute the X component integral as the Y components have opposite // sign and simply cancel each other // // The integral we need to compute is thus: // // X = Integral[-maxTheta,maxTheta]{ cos(theta) dtheta } // X = 2 * sin(maxTheta) // // The Z component is straightforward Z = Integral[-maxTheta,maxTheta]{ dtheta } = 2 maxTheta // float cosMaxTheta = -1.0f; // No clipping at the moment float maxTheta = Mathf.Acos(Mathf.Clamp(cosMaxTheta, -1, 1)); // @TODO: Optimize! Use fast acos or something... float3 F = new float3(2 * Mathf.Sqrt(1 - cosMaxTheta * cosMaxTheta), 0, 2 * maxTheta) / (Mathf.Sqrt(2) * Mathf.TWOPI); float3 F2 = F / -det; return(F2.Length); // // 4) Transform back into LTC space using M^-1 // float3 F2 = invM * F; // @TODO: Optimize => simply return length(F2) = determinant of M^-1 // // // 5) Estimate scalar irradiance //// return -1.0f / det; //// return Mathf.Abs(F2.z); // return F2.Length; }
Primitive BuildSubdividedCube() { // Default example face where B is used to stored the triangle's center and float3 C0 = (new float3(-1.0f, 1.0f, 1.0f) + new float3(-1.0f, -1.0f, 1.0f) + new float3(1.0f, -1.0f, 1.0f)) / 3.0f; float3 C1 = (new float3(-1.0f, 1.0f, 1.0f) + new float3(1.0f, -1.0f, 1.0f) + new float3(1.0f, 1.0f, 1.0f)) / 3.0f; VertexP3N3G3B3T2[] defaultFace = new VertexP3N3G3B3T2[6] { // First triangle new VertexP3N3G3B3T2() { P = new float3(-1.0f, 1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(0, 0) }, new VertexP3N3G3B3T2() { P = new float3(-1.0f, -1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(0, 1) }, new VertexP3N3G3B3T2() { P = new float3(1.0f, -1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(1, 1) }, // 2nd triangle new VertexP3N3G3B3T2() { P = new float3(-1.0f, 1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(0, 0) }, new VertexP3N3G3B3T2() { P = new float3(1.0f, -1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(1, 1) }, new VertexP3N3G3B3T2() { P = new float3(1.0f, 1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(1, 0) }, }; float3[] faceNormals = new float3[6] { -float3.UnitX, float3.UnitX, -float3.UnitY, float3.UnitY, -float3.UnitZ, float3.UnitZ, }; float3[] faceTangents = new float3[6] { float3.UnitZ, -float3.UnitZ, float3.UnitX, float3.UnitX, -float3.UnitX, float3.UnitX, }; Func <float3, float3, float3, float3, float3> lambdaTransform = (float3 _P, float3 _T, float3 _B, float3 _N) => _P.x * _T + _P.y * _B + _P.z * _N; List <VertexP3N3G3B3T2> vertices = new List <VertexP3N3G3B3T2>(); List <uint> indices = new List <uint>(); const uint SUBVIVS_COUNT = 5; VertexP3N3G3B3T2[] temp = new VertexP3N3G3B3T2[3]; // for ( int faceIndex=3; faceIndex < 4; faceIndex++ ) { for (int faceIndex = 0; faceIndex < 6; faceIndex++) { float3 N = faceNormals[faceIndex]; float3 T = faceTangents[faceIndex]; float3 B = N.Cross(T); for (uint triIndex = 0; triIndex < 2; triIndex++) { for (int i = 0; i < 3; i++) { VertexP3N3G3B3T2 V = defaultFace[3 * triIndex + i]; temp[i].P = lambdaTransform(V.P, T, B, N); temp[i].N = lambdaTransform(V.N, T, B, N); temp[i].T = lambdaTransform(V.T, T, B, N); temp[i].B = lambdaTransform(V.B, T, B, N); temp[i].UV = V.UV; } SubdivTriangle(temp, SUBVIVS_COUNT, vertices, indices); } } return(new Primitive(m_device, (uint)vertices.Count, VertexP3N3G3B3T2.FromArray(vertices.ToArray()), indices.ToArray(), Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2)); }
Primitive BuildCube() { // Default example face where B is used to stored the triangle's center and // float3 C0 = (new float3( -1.0f, 1.0f, 1.0f ) + new float3( -1.0f, -1.0f, 1.0f ) + new float3( 1.0f, -1.0f, 1.0f )) / 3.0f; // float3 C1 = (new float3( -1.0f, 1.0f, 1.0f ) + new float3( 1.0f, -1.0f, 1.0f ) + new float3( 1.0f, 1.0f, 1.0f )) / 3.0f; float R = (float)Math.Sqrt(3.0f) / BEND; float3 C0 = new float3(R, 0, 0); float3 C1 = new float3(R, 0, 0); VertexP3N3G3B3T2[] defaultFace = new VertexP3N3G3B3T2[6] { // First triangle new VertexP3N3G3B3T2() { P = new float3(-1.0f, 1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(0, 0) }, new VertexP3N3G3B3T2() { P = new float3(-1.0f, -1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(0, 1) }, new VertexP3N3G3B3T2() { P = new float3(1.0f, -1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C0, UV = new float2(1, 1) }, // 2nd triangle new VertexP3N3G3B3T2() { P = new float3(-1.0f, 1.0f, 1.0f), N = new float3(BEND * -1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(0, 0) }, new VertexP3N3G3B3T2() { P = new float3(1.0f, -1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * -1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(1, 1) }, new VertexP3N3G3B3T2() { P = new float3(1.0f, 1.0f, 1.0f), N = new float3(BEND * 1.0f, BEND * 1.0f, 1.0f).Normalized, T = new float3(0, 0, 1), B = C1, UV = new float2(1, 0) }, }; float3[] faceNormals = new float3[6] { -float3.UnitX, float3.UnitX, -float3.UnitY, float3.UnitY, -float3.UnitZ, float3.UnitZ, }; float3[] faceTangents = new float3[6] { float3.UnitZ, -float3.UnitZ, float3.UnitX, float3.UnitX, -float3.UnitX, float3.UnitX, }; Func <float3, float3, float3, float3, float3> lambdaTransform = (float3 _P, float3 _T, float3 _B, float3 _N) => _P.x * _T + _P.y * _B + _P.z * _N; VertexP3N3G3B3T2[] vertices = new VertexP3N3G3B3T2[6 * 2 * 3]; for (int faceIndex = 0; faceIndex < 6; faceIndex++) { float3 N = faceNormals[faceIndex]; float3 T = faceTangents[faceIndex]; float3 B = N.Cross(T); for (int i = 0; i < 6; i++) { VertexP3N3G3B3T2 V = defaultFace[i]; vertices[6 * faceIndex + i].P = lambdaTransform(V.P, T, B, N); vertices[6 * faceIndex + i].N = lambdaTransform(V.N, T, B, N); vertices[6 * faceIndex + i].T = lambdaTransform(V.T, T, B, N); // vertices[6*faceIndex+i].B = lambdaTransform( V.B, T, B, N ); vertices[6 * faceIndex + i].B = V.B; vertices[6 * faceIndex + i].UV = V.UV; } } uint[] indices = new uint[6 * 2 * 3] { // 0, 1, 2, 0, 2, 3, // 4, 5, 6, 4, 6, 7, // 8, 9, 10, 8, 10, 11, // 12, 13, 14, 12, 14, 15, // 16, 17, 18, 16, 18, 19, // 20, 21, 22, 20, 22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 }; return(new Primitive(m_device, (uint)vertices.Length, VertexP3N3G3B3T2.FromArray(vertices), indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2)); }
/// <summary> /// Computes the 2 radii of the surface tangent to a vertex given a set of neighbor vertices /// </summary> /// <param name="_P"></param> /// <param name="_N"></param> /// <param name="_neighbors"></param> /// <returns></returns> float2 ComputeTangentCurvatureRadii(float3 _P, float3 _N, float3 _T, float3[] _neighbors, bool _debugGraph) { // Compute the square distance between the local neighbors and the double curvature surface of equation f(x,y) = Rt * x² + Rb * y² Func <double, double, float3[], double> SquareDistance = (double _Rt, double _Rb, float3[] _Pns) => { double result = 0.0; foreach (float3 Pn in _Pns) { double Fxy = _Rt * Pn.x * Pn.x + _Rb * Pn.y * Pn.y; double Dz = Fxy - Pn.z; result += Dz * Dz; // result += Dz; } return(result); }; // Transform neighbors into local space float3 B = _T.Cross(_N).Normalized; float3[] neighbors = new float3[_neighbors.Length]; for (int neighborIndex = 0; neighborIndex < _neighbors.Length; neighborIndex++) { float3 D = _neighbors[neighborIndex] - _P; neighbors[neighborIndex].Set(D.Dot(_T), D.Dot(B), D.Dot(_N)); } float2 rangeX = new float2(-100, 100), rangeY = new float2(-1000, 1000); float4 pipo = new float4(1, 0, 0, 1); if (_debugGraph) { graph.WritePixels((uint X, uint Y, ref float4 _color) => { float R0 = rangeX.x + X * (rangeX.y - rangeX.x) / graph.Width; float R1 = rangeY.y + Y * (rangeY.x - rangeY.y) / graph.Height; float sqDistance = (float)SquareDistance(R0, R1, _neighbors); float V = Math.Abs(sqDistance / 200.0f); _color.Set(V, V, V, 1); }); graph.DrawLine(new float4(0, 0, 1, 1), new float2(0, 0.5f * graph.Height), new float2(graph.Width, 0.5f * graph.Height)); graph.DrawLine(new float4(0, 0, 1, 1), new float2(0.5f * graph.Width, 0), new float2(0.5f * graph.Width, graph.Height)); } const float eps = 0.01f; const double tol = 1e-3; // Compute gradient descent double previousRt = -double.MaxValue; double previousRb = -double.MaxValue; double Rt = 0.0, Rb = 0.0; int iterationsCount = 0; double previousSqDistance = double.MaxValue; double bestSqDistance = double.MaxValue; double bestRt = 0.0; double bestRb = 0.0; double stepSize = 10.0; int bestIterationsCount = 0; while (Math.Abs(Rt) < 10000.0f && Math.Abs(Rb) < 10000.0f && iterationsCount < 1000) { // Compute gradient double sqDistance = SquareDistance(Rt, Rb, neighbors); // Central value if (Math.Abs(sqDistance - previousSqDistance) / sqDistance < tol) { break; // Relative error is low enough } double sqDistance_dT = SquareDistance(Rt + eps, Rb, neighbors); double sqDistance_dB = SquareDistance(Rt, Rb + eps, neighbors); // double sqDistance_dTdB = SquareDistance( Rt + eps, Rb + eps, neighbors ); double grad_dT = (sqDistance_dT - sqDistance) / eps; double grad_dB = (sqDistance_dB - sqDistance) / eps; // double grad_dTdB = (sqDistance_dTdB - sqDistance) / eps; // grad_dT = 0.5 * (grad_dT + grad_dTdB); // grad_dB = 0.5 * (grad_dB + grad_dTdB); // Compute intersection with secant Y=0 // double t_T = -sqDistance * (Math.Abs( grad_dT ) > 1e-6 ? 1.0 / grad_dT : (Math.Sign( grad_dT ) * 1e6)); // double t_B = -sqDistance * (Math.Abs( grad_dB ) > 1e-6 ? 1.0 / grad_dB : (Math.Sign( grad_dB ) * 1e6)); double t_T = -stepSize * eps * grad_dT; double t_B = -stepSize * eps * grad_dB; if (t_T * t_T + t_B * t_B < tol * tol) { break; } previousRt = Rt; previousRb = Rb; Rt += t_T; Rb += t_B; iterationsCount++; if (_debugGraph) { graph.DrawLine(pipo, new float2(((float)previousRt - rangeX.x) * graph.Width / (rangeX.y - rangeX.x), ((float)previousRb - rangeY.y) * graph.Height / (rangeY.x - rangeY.y)) , new float2(((float)Rt - rangeX.x) * graph.Width / (rangeX.y - rangeX.x), ((float)Rb - rangeY.y) * graph.Height / (rangeY.x - rangeY.y))); } // Keep best results previousSqDistance = sqDistance; if (sqDistance < bestSqDistance) { bestSqDistance = sqDistance; bestRt = previousRt; bestRb = previousRb; bestIterationsCount = iterationsCount; } } Rt = Math.Max(-10000.0, Math.Min(10000.0, Rt)); Rb = Math.Max(-10000.0, Math.Min(10000.0, Rb)); if (_debugGraph) { panelOutputGraph.m_bitmap = graph.AsBitmap; panelOutputGraph.Refresh(); labelResult.Text = "R = " + Rt.ToString("G4") + ", " + Rb.ToString("G4") + " (" + previousSqDistance.ToString("G4") + ") in " + iterationsCount + " iterations...\r\nBest = " + bestRt.ToString("G4") + ", " + bestRb.ToString("G4") + " (" + bestSqDistance.ToString("G4") + ") in " + bestIterationsCount + " iterations..."; } return(new float2((float)Rt, (float)Rb)); }
private void Generate_CPU(int _RaysCount) { try { tabControlGenerators.Enabled = false; // Half-life basis (Z points outside of the surface, as in normal maps) float3[] basis = new float3[] { new float3((float)Math.Sqrt(2.0 / 3.0), 0.0f, (float)Math.Sqrt(1.0 / 3.0)), new float3((float)-Math.Sqrt(1.0 / 6.0), (float)Math.Sqrt(1.0 / 2.0), (float)Math.Sqrt(1.0 / 3.0)), new float3((float)-Math.Sqrt(1.0 / 6.0), (float)-Math.Sqrt(1.0 / 2.0), (float)Math.Sqrt(1.0 / 3.0)), }; // // 1] Compute normal map // float3 dX = new float3(); // float3 dY = new float3(); // float3 N; // float ddX = floatTrackbarControlPixelSize.Value; // float ddH = floatTrackbarControlHeight.Value; // for ( int Y=0; Y < H; Y++ ) // { // int Y0 = Math.Max( 0, Y-1 ); // int Y1 = Math.Min( H-1, Y+1 ); // for ( int X=0; X < W; X++ ) // { // int X0 = Math.Max( 0, X-1 ); // int X1 = Math.Min( W-1, X+1 ); // // float Hx0 = m_BitmapSource.ContentXYZ[X0,Y].y; // float Hx1 = m_BitmapSource.ContentXYZ[X1,Y].y; // float Hy0 = m_BitmapSource.ContentXYZ[X,Y0].y; // float Hy1 = m_BitmapSource.ContentXYZ[X,Y1].y; // // dX.Set( 2.0f * ddX, 0.0f, ddH * (Hx1 - Hx0) ); // dY.Set( 0.0f, 2.0f * ddX, ddH * (Hy1 - Hy0) ); // // N = dX.Cross( dY ).Normalized; // // m_Normal[X,Y] = new float3( // N.Dot( Basis[0] ), // N.Dot( Basis[1] ), // N.Dot( Basis[2] ) ); // } // // // Update and show progress // UpdateProgress( m_Normal, Y, true ); // } // UpdateProgress( m_Normal, H, true ); float LobeExponent = 4.0f; //floatTrackbarControlLobeExponent.Value; float PixelSize_mm = 1000.0f / floatTrackbarControlPixelDensity.Value; float scale = 0.1f * PixelSize_mm / floatTrackbarControlHeight.Value; // Scale factor to apply to pixel distances so they're renormalized in [0,1], our "heights space"... // Scale *= floatTrackbarControlZFactor.Value; // Cheat Z velocity so AO is amplified! // 2] Build local rays only once int raysCount = integerTrackbarControlRaysCount.Value; float3[,] rays = new float3[3, raysCount]; // Create orthonormal bases to orient the lobe float3 Xr = basis[0].Cross(float3.UnitZ).Normalized; // We can safely use (0,0,1) as the "up" direction since the HL2 basis doesn't have any vertical direction float3 Yr = Xr.Cross(basis[0]); float3 Xg = basis[1].Cross(float3.UnitZ).Normalized; // We can safely use (0,0,1) as the "up" direction since the HL2 basis doesn't have any vertical direction float3 Yg = Xg.Cross(basis[1]); float3 Xb = basis[2].Cross(float3.UnitZ).Normalized; // We can safely use (0,0,1) as the "up" direction since the HL2 basis doesn't have any vertical direction float3 Yb = Xb.Cross(basis[2]); double Exponent = 1.0 / (1.0 + LobeExponent); for (int RayIndex = 0; RayIndex < raysCount; RayIndex++) { // if ( false ) { // double Phi = 2.0 * Math.PI * WMath.SimpleRNG.GetUniform(); // // double Theta = Math.Acos( Math.Pow( WMath.SimpleRNG.GetUniform(), Exponent ) ); // double Theta = Math.PI / 3.0 * WMath.SimpleRNG.GetUniform(); // // float3 RayLocal = new float3( // (float) (Math.Cos( Phi ) * Math.Sin( Theta )), // (float) (Math.Sin( Phi ) * Math.Sin( Theta )), // (float) Math.Cos( Theta ) ); // // Rays[0,RayIndex] = RayLocal.x * Xr + RayLocal.y * Yr + RayLocal.z * Basis[0]; // Rays[1,RayIndex] = RayLocal.x * Xg + RayLocal.y * Yg + RayLocal.z * Basis[1]; // Rays[2,RayIndex] = RayLocal.x * Xb + RayLocal.y * Yb + RayLocal.z * Basis[2]; // } else { double Phi = Math.PI / 3.0 * (2.0 * SimpleRNG.GetUniform() - 1.0); double Theta = 0.49 * Math.PI * SimpleRNG.GetUniform(); rays[0, RayIndex] = new float3( (float)(Math.Cos(Phi) * Math.Sin(Theta)), (float)(Math.Sin(Phi) * Math.Sin(Theta)), (float)Math.Cos(Theta)); Phi = Math.PI / 3.0 * (2.0 * SimpleRNG.GetUniform() - 1.0 + 2.0); Theta = 0.49 * Math.PI * SimpleRNG.GetUniform(); rays[1, RayIndex] = new float3( (float)(Math.Cos(Phi) * Math.Sin(Theta)), (float)(Math.Sin(Phi) * Math.Sin(Theta)), (float)Math.Cos(Theta)); Phi = Math.PI / 3.0 * (2.0 * SimpleRNG.GetUniform() - 1.0 + 4.0); Theta = 0.49 * Math.PI * SimpleRNG.GetUniform(); rays[2, RayIndex] = new float3( (float)(Math.Cos(Phi) * Math.Sin(Theta)), (float)(Math.Sin(Phi) * Math.Sin(Theta)), (float)Math.Cos(Theta)); } rays[0, RayIndex].z *= scale; rays[1, RayIndex].z *= scale; rays[2, RayIndex].z *= scale; } // 3] Compute directional occlusion float4[] scanline = new float4[W]; float4 gammaRGB = float4.Zero; for (uint Y = 0; Y < H; Y++) { for (uint X = 0; X < W; X++) { gammaRGB.x = ComputeAO(0, X, Y, scale, rays); gammaRGB.y = ComputeAO(1, X, Y, scale, rays); gammaRGB.z = ComputeAO(2, X, Y, scale, rays); gammaRGB.w = (gammaRGB.x + gammaRGB.y + gammaRGB.z) / 3.0f; m_sRGBProfile.GammaRGB2LinearRGB(gammaRGB, ref scanline[X]); } m_imageResult.WriteScanline(Y, scanline); // Update and show progress UpdateProgress(m_imageResult, Y, true); } UpdateProgress(m_imageResult, H, true); // m_BitmapResult.Save( "eye_generic_01_disp_hl2.png", ImageFormat.Png ); } catch (Exception _e) { MessageBox("An error occurred during generation:\r\n" + _e.Message, MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { tabControlGenerators.Enabled = true; } }
public double Eval( double[] _newParameters ) { double lobeTheta = _newParameters[0]; double lobeRoughness = _newParameters[1]; double lobeGlobalScale = _newParameters[2]; double lobeFlatten = _newParameters[3]; double maskingImportance = _newParameters[4]; // Flattening is not linear when using the anisotropic lobe model! if ( m_lobeType == LOBE_TYPE.MODIFIED_PHONG_ANISOTROPIC ) lobeFlatten = Math.Pow( 2.0, 4.0 * (lobeFlatten - 1.0)); // in [2e-4, 2e4], log space double invLobeFlatten = 1.0 / lobeFlatten; // Compute constant masking term due to incoming direction double maskingIncoming = Masking( m_direction.z, lobeRoughness ); // Masking( incoming ) // Compute lobe's reflection vector and tangent space using new parameters double cosTheta = Math.Cos( lobeTheta ); double sinTheta = Math.Sin( lobeTheta ); float3 lobe_normal = new float3( (float) (sinTheta * m_incomingDirection_CosPhi), (float) (sinTheta * m_incomingDirection_SinPhi), (float) cosTheta ); float3 lobe_tangent = new float3( (float) -m_incomingDirection_SinPhi, (float) m_incomingDirection_CosPhi, 0.0f ); // Always lying in the X^Y plane float3 lobe_biTangent = lobe_normal.Cross( lobe_tangent ); // Compute sum double phi, theta, cosPhi, sinPhi; double outgoingIntensity_Simulated, length; double outgoingIntensity_Analytical, lobeIntensity; double difference; float3 wsOutgoingDirection = float3.Zero; float3 wsOutgoingDirection2 = float3.Zero; float3 lsOutgoingDirection = float3.Zero; double maskingOutGoing = 0.0; double maskingShadowing; double sum = 0.0; double sum_Simulated = 0.0; double sum_Analytical = 0.0; double sqSum_Simulated = 0.0; double sqSum_Analytical = 0.0; for ( int Y=0; Y < H; Y++ ) { // Formerly used wrong stuff! // // Y = theta bin index = 2.0 * LOBES_COUNT_THETA * pow2( sin( 0.5 * theta ) ) // // We need theta: // theta = 2.0 * Math.Asin( Math.Sqrt( 0.5 * Y / H ) ); // Y = theta bin index = LOBES_COUNT_THETA * (1 - cos( theta ) ) // // We need theta: theta = Math.Acos( 1.0 - (float) Y / H ); cosTheta = Math.Cos( theta ); sinTheta = Math.Sin( theta ); for ( int X=0; X < W; X++ ) { // X = phi bin index = LOBES_COUNT_PHI * X / (2PI) // We need phi: phi = 2.0 * Math.PI * X / W; cosPhi = Math.Cos( phi ); sinPhi = Math.Sin( phi ); // Build simulated microfacet reflection direction in macro-surface space outgoingIntensity_Simulated = m_histogramData[X,Y]; wsOutgoingDirection.Set( (float) (cosPhi * sinTheta), (float) (sinPhi * sinTheta), (float) cosTheta ); // Compute maksing term due to outgoing direction maskingOutGoing = Masking( wsOutgoingDirection.z, lobeRoughness ); // Masking( outgoing ) // Compute projection of world space direction onto reflected direction float Vx = wsOutgoingDirection.Dot( lobe_tangent ); float Vy = wsOutgoingDirection.Dot( lobe_biTangent ); float Vz = wsOutgoingDirection.Dot( lobe_normal ); //Vz = Math.Min( 0.99f, Vz ); float cosTheta_M = Math.Max( 1e-6f, Vz ); // Compute the lobe intensity in local space lobeIntensity = NDF( cosTheta_M, lobeRoughness ); maskingShadowing = 1.0 + maskingImportance * (maskingIncoming * maskingOutGoing - 1.0); // = 1 when importance = 0, = masking when importance = 1 lobeIntensity *= maskingShadowing; // * Masking terms lobeIntensity *= lobeGlobalScale; // Apply additional lobe scaling/flattening length = m_flatteningEval( Vx, Vy, Vz, lobeFlatten, invLobeFlatten ); outgoingIntensity_Analytical = lobeIntensity * length; // Lobe intensity was estimated in lobe space, account for scaling when converting back in world space // Sum the difference between simulated intensity and lobe intensity outgoingIntensity_Analytical *= m_oversizeFactor; // Apply tolerance factor so we're always a bit smaller than the simulated lobe if ( m_fitUsingCenterOfMass ) { double difference0 = outgoingIntensity_Simulated - outgoingIntensity_Analytical; float3 wsLobePosition_Simulated = (float) outgoingIntensity_Simulated * wsOutgoingDirection; float3 wsLobePosition_Analytical = (float) outgoingIntensity_Analytical * wsOutgoingDirection; // Subtract center of mass wsLobePosition_Simulated -= m_centerOfMass; wsLobePosition_Analytical -= m_centerOfMass; // Compute new intensities, relative to center of mass outgoingIntensity_Simulated = wsLobePosition_Simulated.Length; outgoingIntensity_Analytical = wsLobePosition_Analytical.Length; double difference1 = outgoingIntensity_Simulated - outgoingIntensity_Analytical; difference = 0.5 * difference0 + 0.5 * difference1; // difference *= (wsLobePosition_Simulated - wsLobePosition_Analytical).Length; // difference += (wsLobePosition_Simulated - wsLobePosition_Analytical).Length; // We also add the distance between lobe positions so it goes to the best of the 2 minima! } else { difference = outgoingIntensity_Simulated - outgoingIntensity_Analytical; // difference = outgoingIntensity_Simulated / Math.Max( 1e-6, outgoingIntensity_Analytical ) - 1.0; // difference = outgoingIntensity_Analytical / Math.Max( 1e-6, outgoingIntensity_Simulated ) - 1.0; } sum += difference * difference; sum_Simulated += outgoingIntensity_Simulated; sum_Analytical += outgoingIntensity_Analytical; sqSum_Simulated += outgoingIntensity_Simulated*outgoingIntensity_Simulated; sqSum_Analytical += outgoingIntensity_Analytical*outgoingIntensity_Analytical; } } sum /= W * H; // Not very useful since BFGS won't care but I'm doing it anyway to have some sort of normalized sum, better for us humans return sum; }
private void BuildPrimitives() { { VertexPt4[] Vertices = new VertexPt4[4]; Vertices[0] = new VertexPt4() { Pt = new float4( -1, +1, 0, 1 ) }; // Top-Left Vertices[1] = new VertexPt4() { Pt = new float4( -1, -1, 0, 1 ) }; // Bottom-Left Vertices[2] = new VertexPt4() { Pt = new float4( +1, +1, 0, 1 ) }; // Top-Right Vertices[3] = new VertexPt4() { Pt = new float4( +1, -1, 0, 1 ) }; // Bottom-Right ByteBuffer VerticesBuffer = VertexPt4.FromArray( Vertices ); m_Prim_Quad = new Primitive( m_Device, Vertices.Length, VerticesBuffer, null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.Pt4 ); } { VertexP3N3G3B3T2[] Vertices = new VertexP3N3G3B3T2[4]; Vertices[0] = new VertexP3N3G3B3T2() { P = new float3( -1, +1, 0 ), N = new float3( 0, 0, 1 ), T = new float3( 1, 0, 0 ), B = new float3( 0, 1, 0 ), UV = new float2( 0, 0 ) }; // Top-Left Vertices[1] = new VertexP3N3G3B3T2() { P = new float3( -1, -1, 0 ), N = new float3( 0, 0, 1 ), T = new float3( 1, 0, 0 ), B = new float3( 0, 1, 0 ), UV = new float2( 0, 1 ) }; // Bottom-Left Vertices[2] = new VertexP3N3G3B3T2() { P = new float3( +1, +1, 0 ), N = new float3( 0, 0, 1 ), T = new float3( 1, 0, 0 ), B = new float3( 0, 1, 0 ), UV = new float2( 1, 0 ) }; // Top-Right Vertices[3] = new VertexP3N3G3B3T2() { P = new float3( +1, -1, 0 ), N = new float3( 0, 0, 1 ), T = new float3( 1, 0, 0 ), B = new float3( 0, 1, 0 ), UV = new float2( 1, 1 ) }; // Bottom-Right ByteBuffer VerticesBuffer = VertexP3N3G3B3T2.FromArray( Vertices ); m_Prim_Rectangle = new Primitive( m_Device, Vertices.Length, VerticesBuffer, null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3B3T2 ); } { // Build the sphere const int W = 41; const int H = 22; VertexP3N3G3B3T2[] Vertices = new VertexP3N3G3B3T2[W*H]; for ( int Y=0; Y < H; Y++ ) { double Theta = Math.PI * Y / (H-1); float CosTheta = (float) Math.Cos( Theta ); float SinTheta = (float) Math.Sin( Theta ); for ( int X=0; X < W; X++ ) { double Phi = 2.0 * Math.PI * X / (W-1); float CosPhi = (float) Math.Cos( Phi ); float SinPhi = (float) Math.Sin( Phi ); float3 N = new float3( SinTheta * SinPhi, CosTheta, SinTheta * CosPhi ); float3 T = new float3( CosPhi, 0.0f, -SinPhi ); float3 B = N.Cross( T ); Vertices[W*Y+X] = new VertexP3N3G3B3T2() { P = N, N = N, T = T, B = B, UV = new float2( 2.0f * X / W, 1.0f * Y / H ) }; } } ByteBuffer VerticesBuffer = VertexP3N3G3B3T2.FromArray( Vertices ); uint[] Indices = new uint[(H-1) * (2*W+2)-2]; int IndexCount = 0; for ( int Y=0; Y < H-1; Y++ ) { for ( int X=0; X < W; X++ ) { Indices[IndexCount++] = (uint) ((Y+0) * W + X); Indices[IndexCount++] = (uint) ((Y+1) * W + X); } if ( Y < H-2 ) { Indices[IndexCount++] = (uint) ((Y+1) * W - 1); Indices[IndexCount++] = (uint) ((Y+1) * W + 0); } } m_Prim_Sphere = new Primitive( m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3B3T2 ); } { // Build the cube float3[] Normals = new float3[6] { -float3.UnitX, float3.UnitX, -float3.UnitY, float3.UnitY, -float3.UnitZ, float3.UnitZ, }; float3[] Tangents = new float3[6] { float3.UnitZ, -float3.UnitZ, float3.UnitX, -float3.UnitX, -float3.UnitX, float3.UnitX, }; VertexP3N3G3B3T2[] Vertices = new VertexP3N3G3B3T2[6*4]; uint[] Indices = new uint[2*6*3]; for ( int FaceIndex=0; FaceIndex < 6; FaceIndex++ ) { float3 N = Normals[FaceIndex]; float3 T = Tangents[FaceIndex]; float3 B = N.Cross( T ); Vertices[4*FaceIndex+0] = new VertexP3N3G3B3T2() { P = N - T + B, N = N, T = T, B = B, UV = new float2( 0, 0 ) }; Vertices[4*FaceIndex+1] = new VertexP3N3G3B3T2() { P = N - T - B, N = N, T = T, B = B, UV = new float2( 0, 1 ) }; Vertices[4*FaceIndex+2] = new VertexP3N3G3B3T2() { P = N + T - B, N = N, T = T, B = B, UV = new float2( 1, 1 ) }; Vertices[4*FaceIndex+3] = new VertexP3N3G3B3T2() { P = N + T + B, N = N, T = T, B = B, UV = new float2( 1, 0 ) }; Indices[2*3*FaceIndex+0] = (uint) (4*FaceIndex+0); Indices[2*3*FaceIndex+1] = (uint) (4*FaceIndex+1); Indices[2*3*FaceIndex+2] = (uint) (4*FaceIndex+2); Indices[2*3*FaceIndex+3] = (uint) (4*FaceIndex+0); Indices[2*3*FaceIndex+4] = (uint) (4*FaceIndex+2); Indices[2*3*FaceIndex+5] = (uint) (4*FaceIndex+3); } ByteBuffer VerticesBuffer = VertexP3N3G3B3T2.FromArray( Vertices ); m_Prim_Cube = new Primitive( m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2 ); } }
public double Eval(double[] _newParameters) { double lobeTheta = _newParameters[0]; double lobeRoughness = _newParameters[1]; double lobeGlobalScale = _newParameters[2]; double lobeFlatten = _newParameters[3]; double maskingImportance = _newParameters[4]; // Flattening is not linear when using the anisotropic lobe model! if (m_lobeType == LOBE_TYPE.MODIFIED_PHONG_ANISOTROPIC) { lobeFlatten = Math.Pow(2.0, 4.0 * (lobeFlatten - 1.0)); // in [2e-4, 2e4], log space } double invLobeFlatten = 1.0 / lobeFlatten; // Compute constant masking term due to incoming direction double maskingIncoming = Masking(m_direction.z, lobeRoughness); // Masking( incoming ) // Compute lobe's reflection vector and tangent space using new parameters double cosTheta = Math.Cos(lobeTheta); double sinTheta = Math.Sin(lobeTheta); float3 lobe_normal = new float3((float)(sinTheta * m_incomingDirection_CosPhi), (float)(sinTheta * m_incomingDirection_SinPhi), (float)cosTheta); float3 lobe_tangent = new float3((float)-m_incomingDirection_SinPhi, (float)m_incomingDirection_CosPhi, 0.0f); // Always lying in the X^Y plane float3 lobe_biTangent = lobe_normal.Cross(lobe_tangent); // Compute sum double phi, theta, cosPhi, sinPhi; double outgoingIntensity_Simulated, length; double outgoingIntensity_Analytical, lobeIntensity; double difference; float3 wsOutgoingDirection = float3.Zero; float3 wsOutgoingDirection2 = float3.Zero; float3 lsOutgoingDirection = float3.Zero; double maskingOutGoing = 0.0; double maskingShadowing; double sum = 0.0; double sum_Simulated = 0.0; double sum_Analytical = 0.0; double sqSum_Simulated = 0.0; double sqSum_Analytical = 0.0; for (int Y = 0; Y < H; Y++) { // Formerly used wrong stuff! // // Y = theta bin index = 2.0 * LOBES_COUNT_THETA * pow2( sin( 0.5 * theta ) ) // // We need theta: // theta = 2.0 * Math.Asin( Math.Sqrt( 0.5 * Y / H ) ); // Y = theta bin index = LOBES_COUNT_THETA * (1 - cos( theta ) ) // // We need theta: theta = Math.Acos(1.0 - (float)Y / H); cosTheta = Math.Cos(theta); sinTheta = Math.Sin(theta); for (int X = 0; X < W; X++) { // X = phi bin index = LOBES_COUNT_PHI * X / (2PI) // We need phi: phi = 2.0 * Math.PI * X / W; cosPhi = Math.Cos(phi); sinPhi = Math.Sin(phi); // Build simulated microfacet reflection direction in macro-surface space outgoingIntensity_Simulated = m_histogramData[X, Y]; wsOutgoingDirection.Set((float)(cosPhi * sinTheta), (float)(sinPhi * sinTheta), (float)cosTheta); // Compute maksing term due to outgoing direction maskingOutGoing = Masking(wsOutgoingDirection.z, lobeRoughness); // Masking( outgoing ) // Compute projection of world space direction onto reflected direction float Vx = wsOutgoingDirection.Dot(lobe_tangent); float Vy = wsOutgoingDirection.Dot(lobe_biTangent); float Vz = wsOutgoingDirection.Dot(lobe_normal); //Vz = Math.Min( 0.99f, Vz ); float cosTheta_M = Math.Max(1e-6f, Vz); // Compute the lobe intensity in local space lobeIntensity = NDF(cosTheta_M, lobeRoughness); maskingShadowing = 1.0 + maskingImportance * (maskingIncoming * maskingOutGoing - 1.0); // = 1 when importance = 0, = masking when importance = 1 lobeIntensity *= maskingShadowing; // * Masking terms lobeIntensity *= lobeGlobalScale; // Apply additional lobe scaling/flattening length = m_flatteningEval(Vx, Vy, Vz, lobeFlatten, invLobeFlatten); outgoingIntensity_Analytical = lobeIntensity * length; // Lobe intensity was estimated in lobe space, account for scaling when converting back in world space // Sum the difference between simulated intensity and lobe intensity outgoingIntensity_Analytical *= m_oversizeFactor; // Apply tolerance factor so we're always a bit smaller than the simulated lobe if (m_fitUsingCenterOfMass) { double difference0 = outgoingIntensity_Simulated - outgoingIntensity_Analytical; float3 wsLobePosition_Simulated = (float)outgoingIntensity_Simulated * wsOutgoingDirection; float3 wsLobePosition_Analytical = (float)outgoingIntensity_Analytical * wsOutgoingDirection; // Subtract center of mass wsLobePosition_Simulated -= m_centerOfMass; wsLobePosition_Analytical -= m_centerOfMass; // Compute new intensities, relative to center of mass outgoingIntensity_Simulated = wsLobePosition_Simulated.Length; outgoingIntensity_Analytical = wsLobePosition_Analytical.Length; double difference1 = outgoingIntensity_Simulated - outgoingIntensity_Analytical; difference = 0.5 * difference0 + 0.5 * difference1; // difference *= (wsLobePosition_Simulated - wsLobePosition_Analytical).Length; // difference += (wsLobePosition_Simulated - wsLobePosition_Analytical).Length; // We also add the distance between lobe positions so it goes to the best of the 2 minima! } else { difference = outgoingIntensity_Simulated - outgoingIntensity_Analytical; // difference = outgoingIntensity_Simulated / Math.Max( 1e-6, outgoingIntensity_Analytical ) - 1.0; // difference = outgoingIntensity_Analytical / Math.Max( 1e-6, outgoingIntensity_Simulated ) - 1.0; } sum += difference * difference; sum_Simulated += outgoingIntensity_Simulated; sum_Analytical += outgoingIntensity_Analytical; sqSum_Simulated += outgoingIntensity_Simulated * outgoingIntensity_Simulated; sqSum_Analytical += outgoingIntensity_Analytical * outgoingIntensity_Analytical; } } sum /= W * H; // Not very useful since BFGS won't care but I'm doing it anyway to have some sort of normalized sum, better for us humans return(sum); }
public void InitializeCamera( float3 _Position, float3 _Target, float3 _Up ) { // Build the camera matrix float3 At = _Target - _Position; if ( At.LengthSquared > 1e-2f ) { // Normal case m_CameraTargetDistance = At.Length; At /= m_CameraTargetDistance; } else { // Special bad case m_CameraTargetDistance = 0.01f; At = new float3( 0.0f, 0.0f, -1.0f ); } float3 Ortho = _Up.Cross( At ).Normalized; float4x4 CameraMat = float4x4.Identity; CameraMat.r3 = new float4( _Position, 1.0f ); CameraMat.r2 = new float4( At, 0.0f ); CameraMat.r0 = new float4( Ortho, 0.0f ); CameraMat.r1 = new float4( At.Cross( Ortho ), 0.0f ); CameraTransform = CameraMat; // Setup the normalized target distance m_NormalizedTargetDistance = NormalizeTargetDistance( m_CameraTargetDistance ); }
Primitive BuildTorus() { List <VertexP3N3G3B3T2> vertices = new List <VertexP3N3G3B3T2>(); List <uint> indices = new List <uint>(); // Build vertices VertexP3N3G3B3T2 V = new VertexP3N3G3B3T2(); for (uint i = 0; i < SUBDIVS0; i++) { float a0 = 2.0f * (float)Math.PI * i / SUBDIVS0; float3 X = new float3((float)Math.Cos(a0), (float)Math.Sin(a0), 0.0f); float3 Y = new float3(-X.y, X.x, 0.0f); float3 Z = X.Cross(Y); float3 C = RADIUS0 * X; // Center of little ring, around large ring for (uint j = 0; j < SUBDIVS1; j++) { float a1 = 2.0f * (float)Math.PI * j / SUBDIVS1; float3 lsN = new float3((float)Math.Cos(a1), (float)Math.Sin(a1), 0.0f); // float3 lsN2 = new float3( -lsN.y, lsN.x, 0.0f ); float3 N = lsN.x * X + lsN.y * Z; // float3 N2 = lsN2.x * X + lsN2.y * Z; V.P = C + RADIUS1 * N; V.N = N; V.T = Y; V.UV = new float2(4.0f * i / SUBDIVS0, 1.0f * j / SUBDIVS1); vertices.Add(V); } } // Build indices and curvature uint[,] neighborIndices = new uint[3, 3]; float3[] neighbors = new float3[8]; List <float2> curvatures = new List <float2>(); float2 minCurvature = new float2(float.MaxValue, float.MaxValue); float2 maxCurvature = new float2(-float.MaxValue, -float.MaxValue); float2 avgCurvature = float2.Zero; int count = 0; for (uint i = 0; i < SUBDIVS0; i++) { uint Pi = (i + SUBDIVS0 - 1) % SUBDIVS0; uint Ni = (i + 1) % SUBDIVS0; uint ringCurrent = SUBDIVS1 * i; uint ringPrevious = SUBDIVS1 * Pi; uint ringNext = SUBDIVS1 * Ni; for (uint j = 0; j < SUBDIVS1; j++) { uint Pj = (j + SUBDIVS1 - 1) % SUBDIVS1; uint Nj = (j + 1) % SUBDIVS1; neighborIndices[0, 0] = ringPrevious + Pj; neighborIndices[0, 1] = ringPrevious + j; neighborIndices[0, 2] = ringPrevious + Nj; neighborIndices[1, 0] = ringCurrent + Pj; neighborIndices[1, 1] = ringCurrent + j; neighborIndices[1, 2] = ringCurrent + Nj; neighborIndices[2, 0] = ringNext + Pj; neighborIndices[2, 1] = ringNext + j; neighborIndices[2, 2] = ringNext + Nj; // Build 2 triangles indices.Add(neighborIndices[1, 2]); indices.Add(neighborIndices[1, 1]); indices.Add(neighborIndices[2, 1]); indices.Add(neighborIndices[1, 2]); indices.Add(neighborIndices[2, 1]); indices.Add(neighborIndices[2, 2]); // Compute central vertex's curvature VertexP3N3G3B3T2 centerVertex = vertices[(int)neighborIndices[1, 1]]; neighbors[0] = vertices[(int)neighborIndices[0, 0]].P; neighbors[1] = vertices[(int)neighborIndices[1, 0]].P; neighbors[2] = vertices[(int)neighborIndices[2, 0]].P; neighbors[3] = vertices[(int)neighborIndices[0, 1]].P; // neighbors[] = vertices[(int) neighborIndices[1,1]].P; neighbors[4] = vertices[(int)neighborIndices[2, 1]].P; neighbors[5] = vertices[(int)neighborIndices[0, 2]].P; neighbors[6] = vertices[(int)neighborIndices[1, 2]].P; neighbors[7] = vertices[(int)neighborIndices[2, 2]].P; // Single curvature // float curvature = ComputeTangentSphereRadius( centerVertex.P, centerVertex.N, neighbors, false ); // centerVertex.B.x = curvature; // Double curvature float2 curvature = ComputeTangentCurvatureRadii(centerVertex.P, centerVertex.N, centerVertex.T, neighbors, false); centerVertex.B.x = curvature.x; centerVertex.B.y = curvature.y; vertices[(int)neighborIndices[1, 1]] = centerVertex; curvatures.Add(curvature); minCurvature.Min(curvature); maxCurvature.Max(curvature); avgCurvature += curvature; count++; } } avgCurvature /= count; labelMeshInfo.Text = "Curvature Avg. = " + avgCurvature + " - Min = " + minCurvature + " - Max = " + maxCurvature; return(new Primitive(m_device, (uint)vertices.Count, VertexP3N3G3B3T2.FromArray(vertices.ToArray()), indices.ToArray(), Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3B3T2)); }
void BuildPrimitives() { { // Post-process quad List <VertexPt4> Vertices = new List <VertexPt4>(); Vertices.Add(new VertexPt4() { Pt = new float4(-1, +1, 0, 1) }); Vertices.Add(new VertexPt4() { Pt = new float4(-1, -1, 0, 1) }); Vertices.Add(new VertexPt4() { Pt = new float4(+1, +1, 0, 1) }); Vertices.Add(new VertexPt4() { Pt = new float4(+1, -1, 0, 1) }); m_Prim_Quad = new Primitive(m_Device, 4, VertexPt4.FromArray(Vertices.ToArray()), null, Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.Pt4); } { // Sphere Primitive List <VertexP3N3G3T2> Vertices = new List <VertexP3N3G3T2>(); List <uint> Indices = new List <uint>(); const int SUBDIVS_THETA = 80; const int SUBDIVS_PHI = 160; for (int Y = 0; Y <= SUBDIVS_THETA; Y++) { double Theta = Y * Math.PI / SUBDIVS_THETA; float CosTheta = (float)Math.Cos(Theta); float SinTheta = (float)Math.Sin(Theta); for (int X = 0; X <= SUBDIVS_PHI; X++) { double Phi = X * 2.0 * Math.PI / SUBDIVS_PHI; float CosPhi = (float)Math.Cos(Phi); float SinPhi = (float)Math.Sin(Phi); float3 N = new float3(SinTheta * SinPhi, CosTheta, SinTheta * CosPhi); float3 T = new float3(CosPhi, 0.0f, -SinPhi); float2 UV = new float2((float)X / SUBDIVS_PHI, (float)Y / SUBDIVS_THETA); Vertices.Add(new VertexP3N3G3T2() { P = N, N = N, T = T, UV = UV }); } } for (int Y = 0; Y < SUBDIVS_THETA; Y++) { int CurrentLineOffset = Y * (SUBDIVS_PHI + 1); int NextLineOffset = (Y + 1) * (SUBDIVS_PHI + 1); for (int X = 0; X <= SUBDIVS_PHI; X++) { Indices.Add((uint)(CurrentLineOffset + X)); Indices.Add((uint)(NextLineOffset + X)); } if (Y < SUBDIVS_THETA - 1) { Indices.Add((uint)(NextLineOffset - 1)); // Degenerate triangle to end the line Indices.Add((uint)NextLineOffset); // Degenerate triangle to start the next line } } m_Prim_Sphere = new Primitive(m_Device, Vertices.Count, VertexP3N3G3T2.FromArray(Vertices.ToArray()), Indices.ToArray(), Primitive.TOPOLOGY.TRIANGLE_STRIP, VERTEX_FORMAT.P3N3G3T2); } { // Build the cube float3[] Normals = new float3[6] { -float3.UnitX, float3.UnitX, -float3.UnitY, float3.UnitY, -float3.UnitZ, float3.UnitZ, }; float3[] Tangents = new float3[6] { float3.UnitZ, -float3.UnitZ, float3.UnitX, -float3.UnitX, -float3.UnitX, float3.UnitX, }; VertexP3N3G3T2[] Vertices = new VertexP3N3G3T2[6 * 4]; uint[] Indices = new uint[2 * 6 * 3]; for (int FaceIndex = 0; FaceIndex < 6; FaceIndex++) { float3 N = Normals[FaceIndex]; float3 T = Tangents[FaceIndex]; float3 B = N.Cross(T); Vertices[4 * FaceIndex + 0] = new VertexP3N3G3T2() { P = N - T + B, N = N, T = T, // B = B, UV = new float2(0, 0) }; Vertices[4 * FaceIndex + 1] = new VertexP3N3G3T2() { P = N - T - B, N = N, T = T, // B = B, UV = new float2(0, 1) }; Vertices[4 * FaceIndex + 2] = new VertexP3N3G3T2() { P = N + T - B, N = N, T = T, // B = B, UV = new float2(1, 1) }; Vertices[4 * FaceIndex + 3] = new VertexP3N3G3T2() { P = N + T + B, N = N, T = T, // B = B, UV = new float2(1, 0) }; Indices[2 * 3 * FaceIndex + 0] = (uint)(4 * FaceIndex + 0); Indices[2 * 3 * FaceIndex + 1] = (uint)(4 * FaceIndex + 1); Indices[2 * 3 * FaceIndex + 2] = (uint)(4 * FaceIndex + 2); Indices[2 * 3 * FaceIndex + 3] = (uint)(4 * FaceIndex + 0); Indices[2 * 3 * FaceIndex + 4] = (uint)(4 * FaceIndex + 2); Indices[2 * 3 * FaceIndex + 5] = (uint)(4 * FaceIndex + 3); } ByteBuffer VerticesBuffer = VertexP3N3G3T2.FromArray(Vertices); m_Prim_Cube = new Primitive(m_Device, Vertices.Length, VerticesBuffer, Indices, Primitive.TOPOLOGY.TRIANGLE_LIST, VERTEX_FORMAT.P3N3G3T2); } }
/// <summary> /// Builds the surface texture from an actual image file /// </summary> /// <param name="_textureFileName"></param> /// <param name="_pixelSize">Size of a pixel, assuming the maximum height is 1</param> public unsafe void BuildSurfaceFromTexture( string _textureFileName, float _pixelSize ) { if ( m_Tex_Heightfield != null ) m_Tex_Heightfield.Dispose(); // We will create a new one so dispose of the old one... // Read the bitmap int W, H; float4[,] Content = null; using ( Bitmap BM = Bitmap.FromFile( _textureFileName ) as Bitmap ) { W = BM.Width; H = BM.Height; Content = new float4[W,H]; BitmapData LockedBitmap = BM.LockBits( new Rectangle( 0, 0, W, H ), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb ); byte R, G, B, A; for ( int Y=0; Y < H; Y++ ) { byte* pScanline = (byte*) LockedBitmap.Scan0.ToPointer() + Y*LockedBitmap.Stride; for ( int X=0; X < W; X++ ) { // Read in shitty order B = *pScanline++; G = *pScanline++; R = *pScanline++; A = *pScanline++; // Use this if you really need RGBA data // Content[X,Y].Set( R / 255.0f, G / 255.0f, B / 255.0f, A / 255.0f ); // But assuming it's a height field, we only store one component into alpha Content[X,Y].Set( 0, 0, 0, R / 255.0f ); // Use Red as height } } BM.UnlockBits( LockedBitmap ); } // Build normal (shitty version) float Hx0, Hx1, Hy0, Hy1; float3 dNx = new float3( 2.0f * _pixelSize, 0, 0 ); float3 dNy = new float3( 0, 2.0f * _pixelSize, 0 ); float3 N; for ( int Y=0; Y < H; Y++ ) { int pY = (Y+H-1) % H; int nY = (Y+1) % H; for ( int X=0; X < W; X++ ) { int pX = (X+W-1) % W; int nX = (X+1) % W; Hx0 = Content[pX,Y].w; Hx1 = Content[nX,Y].w; Hy0 = Content[X,pY].w; Hy1 = Content[X,nY].w; dNx.z = Hx1 - Hx0; dNy.z = Hy0 - Hy1; // Assuming +Y is upward N = dNx.Cross( dNy ); N = N.Normalized; Content[X,Y].x = N.x; Content[X,Y].y = N.y; Content[X,Y].z = N.z; } } // Build the texture from the array PixelsBuffer Buf = new PixelsBuffer( W*H*16 ); using ( BinaryWriter Writer = Buf.OpenStreamWrite() ) for ( int Y=0; Y < H; Y++ ) for ( int X=0; X < W; X++ ) { float4 pixel = Content[X,Y]; Writer.Write( pixel.x ); Writer.Write( pixel.y ); Writer.Write( pixel.z ); Writer.Write( pixel.w ); } Buf.CloseStream(); m_Tex_Heightfield = new Texture2D( m_Device, W, H, 1, 1, PIXEL_FORMAT.RGBA32_FLOAT, false, false, new PixelsBuffer[] { Buf } ); }
public CellPolygon( float3 _P, float3 _N ) { m_P = _P; m_N = _N; m_T = (float3.UnitY.Cross( m_N )).Normalized; m_B = m_N.Cross( m_T ); // Start with 4 vertices const float R = 10.0f; m_Vertices = new float3[] { m_P + R * (-m_T + m_B), m_P + R * (-m_T - m_B), m_P + R * ( m_T - m_B), m_P + R * ( m_T + m_B), }; }
public void Compute(uint _X, uint _Y) { float2 __Position = new float2(0.5f + _X, 0.5f + _Y); float2 UV = __Position / m_resolution; uint pixelPositionX = (uint)Mathf.Floor(__Position.x); uint pixelPositionY = (uint)Mathf.Floor(__Position.y); float noise = 0.0f; //_tex_blueNoise[pixelPosition & 0x3F]; //PerformIntegrationTest(); // Setup camera ray float3 csView = BuildCameraRay(UV); float Z2Distance = csView.Length; csView /= Z2Distance; float3 wsView = (new float4(csView, 0.0f) * m_camera2World).xyz; // Read back depth, normal & central radiance value from last frame float Z = FetchDepth(__Position, 0.0f); Z -= 1e-2f; // Prevent acnea by offseting the central depth closer // float distance = Z * Z2Distance; float3 wsNormal = m_arrayNormal[0][pixelPositionX, pixelPositionY].xyz.Normalized; // Read back last frame's radiance value that we always can use as a default for neighbor areas float3 centralRadiance = m_arrayIrradiance[0][pixelPositionX, pixelPositionY].xyz; // Compute local camera-space float3 wsPos = m_camera2World[3].xyz + Z * Z2Distance * wsView; float3 wsRight = wsView.Cross(m_camera2World[1].xyz).Normalized; float3 wsUp = wsRight.Cross(wsView); float3 wsAt = -wsView; float4x4 localCamera2World = new float4x4(new float4(wsRight, 0), new float4(wsUp, 0), new float4(wsAt, 0), new float4(wsPos, 1)); // Compute local camera-space normal float3 N = new float3(wsNormal.Dot(wsRight), wsNormal.Dot(wsUp), wsNormal.Dot(wsAt)); N.z = Math.Max(1e-4f, N.z); // Make sure it's never 0! // float3 T, B; // BuildOrthonormalBasis( N, T, B ); // Compute screen radius of gather sphere float screenSize_m = 2.0f * Z * TAN_HALF_FOV; // Vertical size of the screen in meters when extended to distance Z float sphereRadius_pixels = m_resolution.y * m_gatherSphereMaxRadius_m / screenSize_m; sphereRadius_pixels = Mathf.Min(GATHER_SPHERE_MAX_RADIUS_P, sphereRadius_pixels); // Prevent it to grow larger than our fixed limit float radiusStepSize_pixels = Mathf.Max(1.0f, sphereRadius_pixels / MAX_SAMPLES); // This gives us our radial step size in pixels uint samplesCount = Mathf.Clamp((uint)Mathf.Ceiling(sphereRadius_pixels / radiusStepSize_pixels), 1, MAX_SAMPLES); // Reduce samples count if possible float radiusStepSize_meters = sphereRadius_pixels * screenSize_m / (samplesCount * m_resolution.y); // This gives us our radial step size in meters // Start gathering radiance and bent normal by subdividing the screen-space disk around our pixel into Z slices float4 GATHER_DEBUG = float4.Zero; float3 sumIrradiance = float3.Zero; float3 csAverageBentNormal = float3.Zero; // float averageConeAngle = 0.0f; // float varianceConeAngle = 0.0f; float sumAO = 0.0f; for (uint angleIndex = 0; angleIndex < MAX_ANGLES; angleIndex++) { float phi = (angleIndex + noise) * Mathf.PI / MAX_ANGLES; //phi = 0.0f; float2 csDirection; csDirection.x = Mathf.Cos(phi); csDirection.y = Mathf.Sin(phi); // Gather irradiance and average cone direction for that slice float3 csBentNormal; float2 coneAngles; float AO; sumIrradiance += GatherIrradiance(csDirection, localCamera2World, N, radiusStepSize_meters, samplesCount, Z, centralRadiance, out csBentNormal, out coneAngles, out AO, ref GATHER_DEBUG); // if ( AO < -0.01f || AO > 1.01f ) // throw new Exception( "MERDE!" ); csAverageBentNormal += csBentNormal; sumAO += AO; // // We're using running variance computation from https://www.johndcook.com/blog/standard_deviation/ // // Avg(N) = Avg(N-1) + [V(N) - Avg(N-1)] / N // // S(N) = S(N-1) + [V(N) - Avg(N-1)] * [V(N) - Avg(N)] // // And variance = S(finalN) / (finalN-1) // // // float previousAverageConeAngle = averageConeAngle; // averageConeAngle += (coneAngles.x - averageConeAngle) / (2*angleIndex+1); // varianceConeAngle += (coneAngles.x - previousAverageConeAngle) * (coneAngles.x - averageConeAngle); // // previousAverageConeAngle = averageConeAngle; // averageConeAngle += (coneAngles.y - averageConeAngle) / (2*angleIndex+2); // varianceConeAngle += (coneAngles.y - previousAverageConeAngle) * (coneAngles.y - averageConeAngle); } // Finalize bent cone & irradiance csAverageBentNormal = csAverageBentNormal.Normalized; //csAverageBentNormal *= Mathf.PI / MAX_ANGLES; sumIrradiance *= Mathf.PI / MAX_ANGLES; // varianceConeAngle /= 2.0f*MAX_ANGLES - 1.0f; // float stdDeviation = Mathf.Sqrt( varianceConeAngle ); sumAO /= 2.0f * MAX_ANGLES; //if ( sumAO < 0.0f || sumAO > 1.0f ) // throw new Exception( "MERDE!" ); float averageConeAngle = Mathf.Acos(1.0f - sumAO); float varianceConeAngle = 0.0f; // Unfortunately, we don't have a proper value for the variance anymore... :'( float stdDeviation = Mathf.Sqrt(varianceConeAngle); sumIrradiance.Max(float3.Zero); ////////////////////////////////////////////////////////////////////////// // Finalize results m_sumIrradiance = new float4(sumIrradiance, 0); m_bentCone = new float4(Mathf.Max(0.01f, Mathf.Cos(averageConeAngle)) * csAverageBentNormal, 1.0f - stdDeviation / (0.5f * Mathf.PI)); float3 DEBUG_VALUE = new float3(1, 0, 1); DEBUG_VALUE = csAverageBentNormal; DEBUG_VALUE = csAverageBentNormal.x * wsRight - csAverageBentNormal.y * wsUp - csAverageBentNormal.z * wsAt; // World-space normal //DEBUG_VALUE = cos( averageConeAngle ); //DEBUG_VALUE = dot( ssAverageBentNormal, N ); //DEBUG_VALUE = 0.01 * Z; //DEBUG_VALUE = sphereRadius_pixels / GATHER_SPHERE_MAX_RADIUS_P; //DEBUG_VALUE = 0.1 * (radiusStepSize_pixels-1); //DEBUG_VALUE = 0.5 * float(samplesCount) / MAX_SAMPLES; //DEBUG_VALUE = varianceConeAngle; //DEBUG_VALUE = stdDeviation; //DEBUG_VALUE = float3( GATHER_DEBUG.xy, 0 ); //DEBUG_VALUE = float3( GATHER_DEBUG.zw, 0 ); DEBUG_VALUE = GATHER_DEBUG.xyz; //DEBUG_VALUE = N; //m_bentCone = float4( DEBUG_VALUE, 1 ); ////////////////////////////////////////////////////////////////////////// // Finalize bent code debug info m_wsConePosition = wsPos; m_wsConeDirection = csAverageBentNormal.x * wsRight + csAverageBentNormal.y * wsUp + csAverageBentNormal.z * wsAt; //m_wsConeDirection = wsNormal; m_averageConeAngle = averageConeAngle; m_stdDeviation = stdDeviation; }