An atmospheric entry geometry shader written in HLSL.
Lessons learned
I learned to work with HLSL’s geometry shader, and learned about shadow mapping. You can read an in-depth analysis in my paper (linked in project specifications)
Project Specifications
- Engine: Custom Engine
- Time spent: 1 Week
- Paper: http://brianvanhyfte.com/wp-content/uploads/2017/10/Paper.pdf
Code Snippets
//Atmospheric Entry Shader - DX11 //Digital Arts & Entertainment //AtmosphericShader.fx //author: Brian Van Hyfte //class: 2DAE1 //Matrices float4x4 gWorldViewProj : WORLDVIEWPROJECTION; float4x4 gWorld: WORLD; float4x4 gViewProj; float4x4 gViewInverse: VIEWINVERSE; float4x4 gVelocityViewProj; //Color bool gUseTextureDiffuse = false; Texture2D gTextureDiffuse; bool gUseTextureSpecularIntensity = false; Texture2D gTextureSpecularIntensity; float4 gColorDiffuse : COLOR = float4(1.0, 1.0, 1.0, 1.0); float4 gColorAmbient : COLOR = float4(0.05, 0.05, 0.05, 1.0); float gAmbientIntensity = 1.0f; float4 gColorSpecular : COLOR = float4(1.0, 1.0, 1.0, 1.0); float gShininess = 50.0f; //Non-Color maps Texture2D gVelocityShadowMap; Texture2D gNoiseMap; //Shadow float gShadowStrength = 1.f; //Global lighting float3 gLightDirection : DIRECTION = float3(-.577f, -.577f, .577f); float gGlobalLightMultiplier = 0.65f; //Friction-related stuff float3 gVelocityDirection = float3(0, -1, 0); float gVelocity = 1; float gEntryIntensity = 1; float4 gEntryLightColor = float4(1, 0.5f, 0.2f, 1); float4 gEntryLightColorSecond = float4(1, 0.15f, 0, 0.00f); float4 gEntryLightColorBright = float4(1, 0.85f, 0.3f, 0.05f); float gVelocityShadowBias = 0.003f; float gVelMultiplier = 20.f; float gColorModifier = 1.5f; float gUVAdd = 0.f; float gLengthMultiplier = 1.f; //States RasterizerState NoCulling { CullMode = NONE; }; BlendState AlphaBlending { BlendEnable[0] = TRUE; SrcBlend = SRC_ALPHA; DestBlend = INV_SRC_ALPHA; }; DepthStencilState DisableDepthWriting { DepthEnable = TRUE; DepthWriteMask = ZERO; }; //Samplers SamplerState gTextureSampler { Filter = MIN_MAG_MIP_LINEAR; AddressU = WRAP; AddressV = WRAP; }; SamplerComparisonState cmpSampler { Filter = COMPARISON_MIN_MAG_MIP_LINEAR; AddressU = MIRROR; AddressV = MIRROR; ComparisonFunc = LESS_EQUAL; }; //Input / output struct VS_INPUT { float3 pos : POSITION; float3 normal : NORMAL; float2 texCoord : TEXCOORD0; }; struct VS_OUTPUT { float4 pos : SV_POSITION; float3 normal : NORMAL; float2 texCoord : TEXCOORD0; float3 worldPos : TEXCOORD1; float4 lPos : TEXCOORD2; }; //Helper functions float2 TexOffset(int u, int v) { int width, height; gVelocityShadowMap.GetDimensions(width, height); return float2(u * 1.0f / width, v * 1.0f / height); } float CalculateShadow(float4 lightPos, Texture2D shadowMap, float bias) { float4 lpos = float4(lightPos.xyz / lightPos.w, lightPos.w); if (lpos.x < -1.0f || lpos.x > 1.0f || lpos.y < -1.0f || lpos.y > 1.0f || lpos.z < 0.0f || lpos.z > 1.0f) return 1; lpos.x = lpos.x / 2 + 0.5; lpos.y = lpos.y / -2 + 0.5; lpos.z -= bias; float sum; float x, y; for (y = -2; y <= 2; y += 1.0f) { for (x = -2; x <= 2; x += 1.0f) { sum += shadowMap.SampleCmpLevelZero(cmpSampler, lpos.xy + texOffset(x, y), lpos.z); } } float shadowFac = sum / 25.0f; return saturate(shadowFac + 1 - gShadowStrength); } float3 CalculateDiffuse(float3 normal, float2 texCoord, float3 lightDirection) { float3 diffuseColor = gColorDiffuse; //Diffuse Logic float diffuseStrength = saturate(dot(normal, -lightDirection)); diffuseColor *= diffuseStrength; if (gUseTextureDiffuse) { diffuseColor = gTextureDiffuse.Sample(gTextureSampler, texCoord) * diffuseStrength; } return diffuseColor; } float3 CalculateSpecularPhong(float3 viewDirection, float3 normal, float2 texCoord, float3 lightDirection) { float3 specularColor = gColorSpecular; if (gUseTextureSpecularIntensity) { specularColor = gTextureSpecularIntensity.Sample(gTextureSampler, texCoord); } //Specular Specular Logic float3 reflectedVector = reflect(lightDirection, normal); float specularStrength = dot(-viewDirection, reflectedVector); specularStrength = saturate(specularStrength); specularStrength = pow(specularStrength, gShininess); specularColor *= specularStrength; return specularColor; } //--------------// //HEATING EFFECT// //--------------// //VERTEX SHADER //Applies heating effect on the model VS_OUTPUT MainVS(VS_INPUT input) { VS_OUTPUT output = (VS_OUTPUT)0; output.pos = mul(float4(input.pos, 1.0f), gWorldViewProj); output.normal = mul(input.normal, (float3x3)gWorld); output.texCoord = input.texCoord; // Use a float4 for transforming output.worldPos = mul(float4(input.pos, 1.0f), gWorld); output.lPos = mul(float4(input.pos, 1), mul(gWorld, gVelocityViewProj)); return output; } //PIXEL SHADER //Applies heating effect on the model float4 MainPS(VS_OUTPUT input) : SV_TARGET { float3 normal = input.normal; float3 ambient = gColorAmbient*gAmbientIntensity; float3 viewDirection = normalize(input.worldPos.xyz - gViewInverse[3].xyz); //Calculate diffuse and specular global lighting float3 globalLight = CalculateDiffuse(normal, input.texCoord, gLightDirection) + CalculateSpecularPhong(viewDirection, normal, input.texCoord, gLightDirection); globalLight *= gGlobalLightMultiplier; float velShadow = CalculateShadow(input.lPos, gVelocityShadowMap, gVelocityShadowBias); float3 entryHeat = CalculateDiffuse(normal, input.texCoord, gVelocityDirection) + CalculateSpecularPhong(viewDirection, normal, input.texCoord, gVelocityDirection); entryHeat *= gEntryLightColor.rgb*gEntryLightColor.a; entryHeat *= velShadow; //Add shadows so it only shows light on planes that are directly exposed to the velocity vector entryHeat *= gEntryIntensity; //Strength of the light. Atmospheric density can change friction, which means surfaces heat up more entryHeat *= gVelocity; //A faster object has more friction, thus it is hotter return float4(ambient + globalLight + entryHeat, 1); } //------------------------------// //TRAIL EFFECT - GEOMETRY SHADER// //------------------------------// //STRUCTS //data struct GS_DATA { float4 pos : SV_POSITION; float4 color : COLOR; }; //input struct GS_INPUT { float3 pos : POSITION; float3 worldPos : POSITION1; float3 normal : NORMAL; float4 lPos : TEXCOORD1; }; //Helper functions void CreateVertex(inout TriangleStream<GS_DATA> triStream, float3 pos, float4 col) { GS_DATA data = (GS_DATA)0; data.pos = mul(float4(pos, 1.0f), gViewProj); data.color = col; triStream.Append(data); } float GetNoiseLength(float2 uv) { float lengthVar = gNoiseMap.SampleLevel(gTextureSampler, uv, 0); return lengthVar * gLengthMultiplier; } //Pixel Shader float4 VelocityTrailPS(GS_DATA input) : SV_TARGET { return input.color; } //Vertex Shader GS_INPUT VelocityTrailerVS(VS_INPUT input) { GS_INPUT output = (GS_INPUT)0; output.pos = input.pos; output.worldPos = mul(float4(input.pos, 1.0f), gWorld); output.normal = mul(input.normal, (float3x3)gWorld); output.lPos = mul(float4(input.pos, 1), mul(gWorld, gVelocityViewProj)); return output; } [maxvertexcount(6)] void VelocityTrailerGS(triangle GS_INPUT vertex[3], inout TriangleStream<GS_DATA> triStream) { //colors float3 brightCol = gEntryLightColorBright.rgb; float4 col1 = float4(brightCol, gEntryLightColorBright.a)*gVelocity; float4 col2 = lerp(gEntryLightColorBright, gEntryLightColorSecond, gColorModifier)*gVelocity; float4 col3 = gEntryLightColorSecond*gVelocity; float3 normVel = normalize(gVelocityDirection); for (int i = 0; i < 3; i++) { float3 normal = normalize(vertex[i].normal); float velDot = -dot(normal, normVel); float occlusion = CalculateShadow(vertex[i].lPos, gVelocityShadowMap, gVelocityShadowBias); //determining length based on normal if (velDot >= 0 && occlusion>0.9f) { int j = (i + 1) % 3; //Create vertices on the edge itself CreateVertex(triStream, vertex[i].worldPos, col1); CreateVertex(triStream, vertex[j].worldPos, col1); //Calculate length float3 normalized = normalize(vertex[i].pos) + gUVAdd; float randomI = GetNoiseLength(float2(normalized.x, normalized.y)); normalized = normalize(vertex[j].pos) + gUVAdd; float randomJ = GetNoiseLength(float2(normalized.x, normalized.y)); //Create "stacked" quads that create a gradient which simulates heat dissipation CreateVertex(triStream, vertex[i].worldPos + (gVelocity * gVelMultiplier * occlusion * velDot * normVel * randomI), col2); CreateVertex(triStream, vertex[j].worldPos + (gVelocity * gVelMultiplier * occlusion * velDot * normVel * randomJ), col2); CreateVertex(triStream, vertex[i].worldPos + (gVelocity * gVelMultiplier * occlusion * velDot * normVel * gColorModifier * randomI), col3); CreateVertex(triStream, vertex[j].worldPos + (gVelocity * gVelMultiplier * occlusion * velDot * normVel * gColorModifier * randomJ), col3); } } } //Techniques technique11 TechPhong { pass p0 { SetRasterizerState(NoCulling); SetVertexShader(CompileShader(vs_4_0, MainVS())); SetPixelShader(CompileShader(ps_4_0, MainPS())); } pass p1 { SetRasterizerState(NoCulling); SetDepthStencilState(DisableDepthWriting, 0); SetBlendState(AlphaBlending, float4(0.0f, 0.0f, 0.0f, 0.0f), 0xFFFFFFFF); SetVertexShader(CompileShader(vs_4_0, VelocityTrailerVS())); SetGeometryShader(CompileShader(gs_4_0, VelocityTrailerGS())); SetPixelShader(CompileShader(ps_4_0, VelocityTrailPS())); } }