接着上回随笔,这一次在局部光源的计算上做了些优化,包括光强度随距离衰弱的计算和将法向量加入到局部光源的计算中。
视频地址:https://vimeo.com/42946031
截图如下:
下面的是局部光源的Light volumn图
要精确计算局部光源,就必须获取当前物体的位置。这个可以通过将屏幕像素从投影坐标转换到视角坐标,再传入当前光源在视角坐标系中的位置,这样入射光线向量就有了。法向量可以从之前保持在贴图读取。两个向量作dot计算可以得出光照强度。同理,计算光源和物体位置的距离除以最大光照范围可以得到光强度衰减值(当前用的是简易的线性衰减)。此处点光源没有按精确的入射光来计算,原因是物体可能在光源范围里,但是却背朝光源中心点,虽然在光照范围内,但我们正向看时会比较奇怪。所以用了衰减系数乘以光的颜色来计算,类似全局光来处理。cone light的话方向我是看作为(0,-1, 0), 效果还可以。这部分计算是在PS_LitScene中。
现在光照的计算还比较简单,之后可以还可以用更复杂的模型。其实关键就是获取的物体的位置,之后都好计算了。
SamplerState g_samWrap { Filter = MIN_MAG_MIP_LINEAR; AddressU = Wrap; AddressV = Wrap; }; SamplerState g_samClamp { Filter = MIN_MAG_MIP_LINEAR; AddressU = Clamp; AddressV = Clamp; }; DepthStencilState DisableDepth { DepthEnable = FALSE; DepthWriteMask = ZERO; }; DepthStencilState EnableDepth { DepthEnable = TRUE; DepthWriteMask = ALL; DepthFunc = Less_Equal;//set to less equal since texture background is ini as 1.0 }; BlendState NoBlending { AlphaToCoverageEnable = FALSE; BlendEnable[0] = FALSE; }; BlendState AlphaBlendingOn { BlendEnable[0] = TRUE; SrcBlend = SRC_ALPHA; DestBlend = INV_SRC_ALPHA; }; BlendState AdditiveBlending { AlphaToCoverageEnable = FALSE; BlendEnable[0] = TRUE; SrcBlend = ONE; DestBlend = ONE; BlendOp = ADD; SrcBlendAlpha = ZERO; DestBlendAlpha = ZERO; BlendOpAlpha = ADD; RenderTargetWriteMask[0] = 0x0F; }; RasterizerState DisableCulling { //FillMode = WIREFRAME; CullMode = NONE; }; RasterizerState EnableCulling { //FillMode = WIREFRAME; CullMode = BACK; }; RasterizerState FrontCulling { //FillMode = WIREFRAME; CullMode = FRONT; }; BlendState DisableFrameBuffer { BlendEnable[0] = FALSE; RenderTargetWriteMask[0] = 0x0; }; BlendState EnableFrameBuffer { BlendEnable[0] = FALSE; RenderTargetWriteMask[0] = 0x0F; }; DepthStencilState TwoSidedStencil { DepthEnable = true; DepthWriteMask = ZERO;//Turn off writes to the depth-stencil buffer. DepthFunc = Less; // Setup stencil states StencilEnable = true; StencilReadMask = 0xFFFFFFFF; StencilWriteMask = 0xFFFFFFFF; BackFaceStencilFunc = Always;// how stencil data is compared against existing stencil data.Always pass the comparison. BackFaceStencilDepthFail = Incr;// describes the stencil operation to perform when stencil testing passes and depth testing fails. BackFaceStencilPass = Keep;// describes the stencil operation to perform when stencil testing and depth testing both pass. BackFaceStencilFail = Keep;//describes the stencil operation to perform when stencil testing fails. FrontFaceStencilFunc = Always; FrontFaceStencilDepthFail = Decr; FrontFaceStencilPass = Keep; FrontFaceStencilFail = Keep; }; DepthStencilState RenderNonShadows { DepthEnable = true; DepthWriteMask = ZERO; DepthFunc = Less_Equal; StencilEnable = true; StencilReadMask = 0xFFFFFFFF;//read stencil buffer to check if lit or not StencilWriteMask = 0x0; FrontFaceStencilFunc = Less_Equal;//If the source data is greater (multipay light)or equal to the destination data, the comparison passes. FrontFaceStencilPass = Keep;//Keep the existing stencil data. FrontFaceStencilFail = Zero;//Set the stencil data to 0. BackFaceStencilFunc = Never;//Never pass the comparison. BackFaceStencilPass = Zero; BackFaceStencilFail = Zero; }; Texture2D g_ModelTexture; Texture2D g_NormDepthTexture; Texture2D g_DiffuseTexture; matrix World; matrix View; matrix Projection; matrix g_InvProj; float4 g_AmbientColor = float4(0.1f, 0.1f, 0.1f, 1.0f); float4 g_DirLightColor = float4(0.3f, 0.3f, 0.3f, 1.0f); float3 g_DirLightDir = float3(0.0f, 1.0f, -1.0f); float4 g_LocalLightColor; float3 g_LocalLightPos; bool g_LocalLightType; //---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- struct VS_MODEL_INPUT { float4 Pos : POSITION; float2 Tex : TEXCOORD0; float3 Norm : NORMAL; }; struct PS_MODEL_INPUT { float4 Pos :SV_POSITION; float2 Tex : TEXCOORD0; float3 Norm : TEXCOORD1; }; struct PS_MODEL_OUTPUT { float4 NormDepth : SV_Target0; float4 DiffuseColor : SV_Target1; }; //---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- struct VS_QUAD_INPUT { float4 Pos :POSITION; float2 Tex : TEXCOORD0; }; struct PS_SCENE_INPUT { float4 Pos :SV_POSITION; float2 Tex : TEXCOORD0; }; struct PS_SCENE_OUTPUT { float4 Color : SV_Target; float Depth : SV_Depth; }; //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ struct PS_LIGHT_MODEL_INPUT { float4 Pos : SV_POSITION; float2 Tex : TEXCOORD0; float4 LightPos : TEXCOORD1; //light original pos in view space }; //----------------------------------------------------------------------------------------render scene to gbuffer------------------------------------------------------------------------------------- PS_MODEL_INPUT VS_SCENE(VS_MODEL_INPUT input) { PS_MODEL_INPUT output = (PS_MODEL_INPUT)0; output.Pos = input.Pos; output.Pos.w = 1; output.Pos = mul( output.Pos, World ); output.Pos = mul( output.Pos, View ); output.Pos = mul( output.Pos, Projection ); output.Pos = output.Pos / output.Pos.w; output.Norm = input.Norm; output.Tex = input.Tex; return output; } PS_MODEL_OUTPUT PS_SCENE(PS_MODEL_INPUT input) { PS_MODEL_OUTPUT output = (PS_MODEL_OUTPUT)0; float4 NormDepth = 0; NormDepth.rgb = input.Norm;//normal NormDepth.a = input.Pos.z;//depth output.NormDepth = NormDepth; output.DiffuseColor = g_ModelTexture.Sample( g_samWrap, input.Tex ); return output; } //-------------------------------------------------------------------------------------add ambient light to scene---------------------------------------------------------------------------------------- PS_SCENE_INPUT VS_Ambient(VS_QUAD_INPUT input) { PS_SCENE_INPUT output = (PS_SCENE_INPUT)0; float2 Pos = input.Pos.xy; output.Pos = float4(Pos.xy, 0, 1); output.Tex.x = 0.5 * (1 + Pos.x); output.Tex.y = 0.5 * (1 - Pos.y); return output; } PS_SCENE_OUTPUT PS_Ambient(PS_SCENE_INPUT input) { PS_SCENE_OUTPUT output = (PS_SCENE_OUTPUT)0; float4 DiffuseColor = g_DiffuseTexture.Sample( g_samClamp, input.Tex ); float4 NormalDepth = g_NormDepthTexture.Sample( g_samClamp, input.Tex ); float3 Normal = NormalDepth.rgb; float Depth = NormalDepth.a; float4 Pos = input.Pos; clip(Depth - 0.0001); output.Color = (g_AmbientColor + dot(normalize(g_DirLightDir), Normal) * g_DirLightColor) * DiffuseColor; output.Color.a = 1; output.Depth = Depth; return output; } //--------------------------------------------------------------------------------------calcuate depth test and set stencil value-------------------------------------------------------------------------------- PS_LIGHT_MODEL_INPUT VS_LightModel(VS_MODEL_INPUT input) { PS_LIGHT_MODEL_INPUT output = (PS_LIGHT_MODEL_INPUT)0; output.Pos = input.Pos; output.Pos.w = 1; output.Pos = mul( output.Pos, World ); output.Pos = mul( output.Pos, View ); output.Pos = mul( output.Pos, Projection ); output.Pos = output.Pos / output.Pos.w; //get the tex coord of the light model on canvas output.Tex = (output.Pos.xy + 1.0f) * 0.5f; output.Tex.y = 1 - output.Tex.y; float4 LightPos = float4(g_LocalLightPos, 1); output.LightPos = mul( LightPos, View ); output.LightPos = output.LightPos / output.LightPos.w; return output; } float4 PS_CalRadium(PS_LIGHT_MODEL_INPUT input) : SV_Target { float4 vloumnColor = g_LocalLightColor; vloumnColor.a = 0.1; return vloumnColor; } //-------------------------------------------------------------------------------------lit the area where stencil value is 1---------------------------------------------------------------------------------------- float4 PS_LitScene(PS_LIGHT_MODEL_INPUT input) : SV_Target { float4 finalColor = 0; float4 NormalDepth = g_NormDepthTexture.Sample( g_samClamp, input.Tex ); float2 PosXY = input.Tex * 2.0f - 1.0f;//tex coord to proj coord PosXY.y *= -1;//back to ori coord float4 PixelProjPos = float4(PosXY, NormalDepth.a, 1.0f);//projection space float4 PixelViewPos = mul(PixelProjPos, g_InvProj); PixelViewPos = PixelViewPos / PixelViewPos.w; //the dis between the light and the scene obj, not with the light volumn !!!!! ori back ground depth is set to 1. float dis = distance(PixelViewPos.xyz, input.LightPos.xyz); float faint = 0; if(g_LocalLightType)//cone light faint = 1.0 - min(dis / 0.2f, 1.0f);//the maximum range of the local light, linear faint else faint = 1.0 - min(dis / 0.1f, 1.0f); float4 DiffuseColor = g_DiffuseTexture.Sample( g_samClamp, input.Tex ); if(g_LocalLightType) finalColor = g_LocalLightColor * dot( normalize( float3(0.0f, -1.0f, 0.0f) ), NormalDepth.rgb ) * faint; else finalColor = g_LocalLightColor * DiffuseColor * faint;//g_LocalLightColor * dot( normalize( input.LightPos.xyz - PixelViewPos.xyz ), NormalDepth.rgb ) * faint; finalColor.a = 1; return finalColor; } //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- technique10 RenderScene { pass p0 { SetVertexShader( CompileShader( vs_4_0, VS_SCENE() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, PS_SCENE() ) ); SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetDepthStencilState( EnableDepth, 0 ); SetRasterizerState( EnableCulling ); //SetRasterizerState( FrontCulling ); } } technique10 AmbientLighting { pass p0 { SetVertexShader( CompileShader( vs_4_0, VS_Ambient() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, PS_Ambient() ) ); SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetDepthStencilState( EnableDepth, 0 );//render all model on texture, no depth test is needed SetRasterizerState( EnableCulling ); } } technique10 CalRadium { pass p0 { SetVertexShader( CompileShader( vs_4_0, VS_LightModel() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, PS_CalRadium() ) ); //SetBlendState( AlphaBlendingOn, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetBlendState( DisableFrameBuffer, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );//don't render color to render target SetDepthStencilState( TwoSidedStencil, 1 ); //do depth fail test, write to stencil buffer SetRasterizerState( DisableCulling );//no culling, so both front and back face could be tested } } technique10 LitScene { pass p0 { SetVertexShader( CompileShader( vs_4_0, VS_LightModel() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, PS_LitScene() ) ); SetBlendState( AdditiveBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF ); SetDepthStencilState( RenderNonShadows, 1); //when stencil value is 0, stencil fun pass and lit that pixel SetRasterizerState( EnableCulling ); } }