   Geometry Shader
  In the article I am using to make an attempt at per pixel lighting, they optionally use a geometry shader to compute tangent, bitangent, and normals for a triangle.This sounds excellant to me, as it saves me the hassle of trying to figure out how to compute or export this data from my 3d modeler.However, I don't understand how I access and use the output of the geometry shader.I thought things got run in this order:VertexShaderGeometry ShaderPixel Shader.Thier previous code handled world to tangent space transformation of light diretion and view direction in the vertex shader and assumed you had per vertex tangent, bitangent, and normals.I am under the impression that if I use this geometry shader code, I instead transform the light direction and view direction in the pixel shader using the transform made by the geometry shader, after multiplying that transform by the world matrix. Where is the geometry shader output stored? How do I get it in my pixel shader?The geometry shader in question:float3x3 ComputeTangentSpaceMatrix  (  in float3 pos[3],  in float2 tc[3]   ){    float3 A = pos[1] - pos[0];    float3 B = pos[2] - pos[0];float2 P = tc[1] - tc[0];    float2 Q = tc[2] - tc[0];float fraction = 1.0f / ( P.x * Q.y - Q.x * P.y );float3 normal = normalize( cross( A, B ) );float3 tangent = float3    (   (Q.y * A.x - P.y * B.x) * fraction,   (Q.y * A.y - P.y * B.y) * fraction,   (Q.y * A.z - P.y * B.z) * fraction    );float3 bitangent = float3    (   (P.x * B.x - Q.x * A.x) * fraction,   (P.x * B.y - Q.x * A.y) * fraction,   (P.x * B.z - Q.x * A.z) * fraction    );// Some simple aliases    float NdotT = dot( normal, tangent );    float NdotB = dot( normal, bitangent );    float TdotB = dot( tangent, bitangent );// Apply Gram-Schmidt orthogonalization    tangent    = tangent - NdotT * normal;    bitangent  = bitangent - NdotB * normal - TdotB * tangent;// Pack the vectors into the matrix output    float3x3 tsMatrix;tsMatrix[0] = tangent;    tsMatrix[1] = bitangent;    tsMatrix[2] = normal;return tsMatrix;}Or maybe this is code that is just a function that needs to be called by an actual geomtery shader? I've never seen a geometry shader yet...This is my incomplete vertex and pixel shader before reading this part of the article. I don't know how to change it up to use the code above://--------------------------------------------------------------------------------------// File: default.fx////--------------------------------------------------------------------------------------#include "EffectPool.fxh"matrix world : World;//-------------------// Diffuse Variables//// Diffuse Color can be a single color or sampled from a texturefloat4    diffuseColor;Texture2D diffuseTexture;SamplerState samplerLinear{   Filter = MIN_MAG_MIP_LINEAR;   AddressU = Wrap;   AddressV = Wrap;};//-------------------// Specular Variables//// Specular Color can be a single color or sampled from a texturefloat4    specularColor;Texture2D specularTexture;floatspecularExponent;//------------------// Normal Map//// Depending on the technique a normal map may be provided or a height map may be provided and// normals will be calculated from the provided height dataTexture2D normalMap;    // Expects DXGI_FORMAT_R8G8B8_UNORMTexture2D heightMap;    ////------------------// Lighting Variables//float3 cameraPosition;  // Position of the camera in world spacefloat3 lightDir;   // Direction vector of the light in world space (See TODO, where this is used)//////// CONNECTOR DATA STRUCTURES ////////////* Data from application vertex buffers */struct VS_INPUT{   float3   position  : POSITION;   float3   tangent   : TANGENT;   float3   biTangent : BITANGENT;   float3   normal : NORMAL;   float2   uv   : TEXCOORD;};/* Data passed from vertex shader to pixel shader */struct VS_OUTPUT{   float4 position : SV_POSITION;   float2 texCoord : TEXCOORD;   float3 lightDir;   float3 viewDir;};//--------------------------------------------------------------------------------------// Normal Vector Lookup//--------------------------------------------------------------------------------------float3 FetchNomalVector(float2 texCoord, uniform bool readFromTexture, uniform bool useSobelFilter){   // Precomputed look-up   if( readFromTexture )   { float3 normal = texNormalMap.Sample(DefaultSampler, texCoord).rgb;  // Normal texture is stored in DXGI_FORMAT_R8G8B8_UNORM, so each component is in the range 0.0 to 1.0, // whereas the vector we want should be in the range -1.0 to 1.0. The multiply by 2 and subtract 1  // maps those vectors correctly return normalize( normal * 2.0f - 1.0f );   }   else   { // Sobel Filter if( useSobelFilter ) {    float2 o00 = texCoord + float2( -vPixelSize.x, -vPixelSize.y);    float2 o10 = texCoord + float2(0.0f, -vPixelSize.y);    float2 o20 = texCoord + float2(  vPixelSize.x, -vPixelSize.y);   float2 o01 = texCoord + float2( -vPixelSize.x, 0.0f);    float2 o21 = texCoord + float2(  vPixelSize.x, 0.0f);   float2 o02 = texCoord + float2( -vPixelSize.x,  vPixelSize.y);    float2 o12 = texCoord + float2(0.0f,  vPixelSize.y);    float2 o22 = texCoord + float2(  vPixelSize.x,  vPixelSize.y);   // Get eight samples surrounding the current pixel    float h00 = texHeightMap.Sample( DefaultSampler, o00).r;    float h10 = texHeightMap.Sample( DefaultSampler, o10).r;    float h20 = texHeightMap.Sample( DefaultSampler, o20).r;   float h01 = texHeightMap.Sample( DefaultSampler, o01).r;    float h21 = texHeightMap.Sample( DefaultSampler, o21).r;   float h02 = texHeightMap.Sample( DefaultSampler, o02).r;    float h12 = texHeightMap.Sample( DefaultSampler, o12).r;    float h22 = texHeightMap.Sample( DefaultSampler, o22).r;   // Evaluate the Sobel filters    float Gx = h00 - h20 + 2.0f * h01 - 2.0f * h21 + h02 - h22;    float Gy = h00 + 2.0f * h10 + h20 - h02 - 2.0f * h12 - h22;   // Generate the missing Z    float Gz = 0.5f * sqrt(1.0f - Gx * Gx - Gy * Gy);   return normalize( float3( 2.0f * Gx, 2.0f * Gy, Gz); }  // 3 Sample Evaluation else {    float o1 = float2(vPixelSize.x, 0.0f);    float o2 = float2(0.0f, vPixelSize.y);   float h0 = texHeightMap.Sample( DefaultSampler, texCoord).r;    float h1 = texHeightMap.Sample( DefaultSampler, texCoord + o1).r    float h2 = texHeightMap.Sample( DefaultSampler, texCoord + 02).r   float3 v01 = float3( o1, h1 - h0);    float3 v02 = float3( o2, h2 - h0);   float n = cross(v01, v02);   // Can be useful to scale the Z component to tweak the amount bumps show up.    // Less than 1.0 will make them more apparent. Greater than 1.0 will smooth them out    n.z *= 0.5f;   return normalize(n);  }}//--------------------------------------------------------------------------------------// Lighting Models//--------------------------------------------------------------------------------------//--------------------------------------------------------------------------------------// Blinn Phong//float4 blinn_phong(in float3 normal, in float3 viewer, in float3 light){   float3 half_vector = normalize(light + viewer);   float  HdotN  = max(0.0f, dot(half_vector, normal));   float3 specular    = specularColor * pow(HdotN, specularExponent);   float3 diffuse= diffuseColor * max(0.0f, dot(normal, light)); return float4(diffuse + specular, 1.0f);}//--------------------------------------------------------------------------------------// Vertex Shader//-------------------------------------------------------------------------------------- VS_OUTPUT VS( VS_INPUT input ){   VS_OUTPUT output = (VS_OUTPUT)0; // Transform the incoming model-space position to projection space   matrix worldViewProjection = mul( mul(world, view), projection);   output.position  = mul( float4(input.position, 1.0f), worldViewProjection); // The world space vertex position is useful for later calculations   float3 positionWorld = mul( float4(input.position, 1.0f), world).xyz; // The per-vertex attributes are in model space, so transform them to world space   // before generating a world->tangent space transformation   float3 vTangentWS   = mul( input.tangent, float(3x3)world);   float3 vBiTangentWS = mul( input.biTangent, float(3x3) world);   float3 vNormalWS    = mul( input.normal, float(3x3) world); // Generate a world space to tangent space transformation   float3x3 worldToTangentSpace;   worldToTangentSpace[0] = normalize( vTangentWS );   worldToTangentSpace[1] = normalize( vBiTangentWS );   worldToTangentSpace[2] = normalize( vnormalWS ); // Transform inputs to the Pixel Shader into tangent space   output.lightDir = mul( worldToTangentSpace, -lightDir);   // TODO - Not sure if light dir should remain flipped or not, depends on app output.viewDir  = cameraPosition - positionWorld;   output.viewDir  = mul( worldToTangentSpace, output.lightDir); output.texCoord = input.texCoord;}//--------------------------------------------------------------------------------------// Pixel Shader//--------------------------------------------------------------------------------------float4 PS( in VS_OUTPUT input) : SV_Target{float3 view  = normalize(input.viewDir);   float3 light = normalize(input.lightDir); // Determine the offset for this pixel   float2 offset = ComputeOffset(input.texcoord, view); // Retrieve the normal   float3 normal = FetchNormalVector(input.texcoord, true); // Evaluate the Lighting model   float4 color = ComputeIllumination(normal, view, light); return color; }//--------------------------------------------------------------------------------------// Techniques//--------------------------------------------------------------------------------------//--------------------------------------------------------------------------------------// Renders an object at 100% opacity, with no lights or shadows//technique10 RenderDefault{    pass P0    {   SetVertexShader( CompileShader( vs_4_0, VS() ) );   SetGeometryShader( NULL );   SetPixelShader( CompileShader( ps_4_0, PS() ) );    }}