/// <summary> /// /// </summary> /// <param name="prof"></param> /// <param name="terrain"></param> /// <param name="tt"></param> /// <param name="source"></param> protected override void GenerateVpHeader( SM2Profile prof, Terrain terrain, TechniqueType tt, ref string source ) { source += "void main_vp(\n" + "float4 pos : POSITION,\n" + "float2 uv : TEXCOORD0,\n"; if ( tt != TechniqueType.RenderCompositeMap ) { source += "float2 delta : TEXCOORD1,\n"; // lodDelta, lodThreshold } source += "uniform float4x4 worldMatrix,\n" + "uniform float4x4 viewProjMatrix,\n" + "uniform float2 lodMorph,\n"; // morph amount, morph LOD target // uv multipliers uint maxLayers = prof.GetMaxLayers( terrain ); uint numLayers = Utility.Min( maxLayers, (uint)terrain.LayerCount ); uint numUVMutipliers = ( numLayers/4 ); if ( numLayers%4 != 0 ) { ++numUVMutipliers; } for ( uint i = 0; i < numUVMutipliers; ++i ) { source += "uniform float4 uvMul" + i + ", \n"; } source += "out float4 oPos : POSITION,\n" + "out float2 oUV : TEXCOORD0, \n" + "out float4 oPosObj : TEXCOORD1 \n"; // layer UV's premultiplied, packed as xy/zw uint numUVSets = numLayers/2; if ( numLayers%2 != 0 ) { ++numUVSets; } uint texCoordSet = 2; if ( tt != TechniqueType.LowLod ) { for ( uint i = 0; i < numUVSets; ++i ) { source += ", out float4 oUV" + i + " : TEXCOORD" + texCoordSet++ + "\n"; } } if ( prof.Parent.DebugLevel != 0 && tt != TechniqueType.RenderCompositeMap ) { source += ", out float2 lodInfo : TEXCOORD" + texCoordSet++ + "\n"; } source += ")\n" + "{\n" + " float4 worldPos = mul(worldMatrix, pos);\n" + " oPosObj = pos;\n"; if ( tt != TechniqueType.RenderCompositeMap ) { // determine whether to apply the LOD morph to this vertex // we store the deltas against all vertices so we only want to apply // the morph to the ones which would disappear. The target LOD which is // being morphed to is stored in lodMorph.y, and the LOD at which // the vertex should be morphed is stored in uv.w. If we subtract // the former from the latter, and arrange to only morph if the // result is negative (it will only be -1 in fact, since after that // the vertex will never be indexed), we will achieve our aim. // sign(vertexLOD - targetLOD) == -1 is to morph source += " float toMorph = -min(0, sign(delta.y - lodMorph.y));\n"; // this will either be 1 (morph) or 0 (don't morph) if ( prof.Parent.DebugLevel != 0 ) { // x == LOD level (-1 since value is target level, we want to display actual) source += "lodInfo.x = (lodMorph.y - 1) / " + terrain.NumLodLevels + ";\n"; // y == LOD morph source += "lodInfo.y = toMorph * lodMorph.x;\n"; } //morph switch ( terrain.Alignment ) { case Alignment.Align_X_Y: break; case Alignment.Align_X_Z: source += " worldPos.y += delta.x * toMorph * lodMorph.x;\n"; break; case Alignment.Align_Y_Z: break; } } // generate UVs if ( tt == TechniqueType.LowLod ) { //passtrough source += " oUV = uv;\n"; } else { for ( uint i = 0; i < numUVSets; ++i ) { uint layer = i*2; uint uvMulIdx = layer/4; source += " oUV" + i + ".xy = " + " uv.xy * uvMul" + uvMulIdx + "." + GetChannel( layer ) + ";\n"; source += " oUV" + i + ".zw = " + " uv.xy * uvMul" + uvMulIdx + "." + GetChannel( layer + 1 ) + ";\n"; } } }
/// <summary> /// /// </summary> /// <param name="prof"></param> /// <param name="terrain"></param> /// <param name="tt"></param> /// <param name="source"></param> protected virtual void GenerateFragmetProgramSource( SM2Profile prof, Terrain terrain, TechniqueType tt, ref string source ) { GenerateFpHeader( prof, terrain, tt, ref source ); if ( tt != TechniqueType.LowLod ) { uint maxLayers = prof.GetMaxLayers( terrain ); uint numLayers = Utility.Min( maxLayers, (uint)terrain.LayerCount ); for ( uint i = 0; i < numLayers; ++i ) { GenerateFpLayer( prof, terrain, tt, i, ref source ); } } GenerateFpFooter( prof, terrain, tt, ref source ); }
/// <summary> /// /// </summary> /// <param name="prof"></param> /// <param name="terrain"></param> /// <param name="tt"></param> /// <param name="gpuparams"></param> public virtual void UpdateVpParams( SM2Profile prof, Terrain terrain, TechniqueType tt, GpuProgramParameters gpuparams ) { gpuparams.IgnoreMissingParameters = true; uint maxLayers = prof.GetMaxLayers( terrain ); uint numLayers = Utility.Min( maxLayers, (uint)terrain.LayerCount ); uint numUVMul = numLayers/4; if ( numUVMul%4 == 0 ) { ++numUVMul; } for ( uint i = 0; i < numUVMul; ++i ) { var uvMul = new Vector4( terrain.GetLayerUVMultiplier( (byte)( i*4 ) ), terrain.GetLayerUVMultiplier( (byte)( i*4 + 1 ) ), terrain.GetLayerUVMultiplier( (byte)( i*4 + 2 ) ), terrain.GetLayerUVMultiplier( (byte)( i*4 + 3 ) ) ); #if true gpuparams.SetNamedConstant( "uvMul" + i.ToString(), uvMul ); #endif } }
/// <summary> /// /// </summary> /// <param name="prof"></param> /// <param name="terrain"></param> /// <param name="tt"></param> /// <param name="source"></param> protected override void GenerateFpHeader( SM2Profile prof, Terrain terrain, TechniqueType tt, ref string source ) { // Main header source += // helpers "float4 expand(float4 v)\n" + "{ \n" + " return v * 2 - 1;\n" + "}\n\n\n" + "float4 main_fp(\n" + "float2 uv : TEXCOORD0,\n" + "float4 position : TEXCOORD1,\n"; // UV's premultiplied, packed as xy/zw uint maxLayers = prof.GetMaxLayers( terrain ); uint numBlendTextures = Utility.Min( terrain.GetBlendTextureCount( (byte)maxLayers ), terrain.GetBlendTextureCount() ); uint numLayers = Utility.Min( maxLayers, (uint)terrain.LayerCount ); uint numUVSets = numLayers/2; if ( numLayers%2 != 0 ) { ++numUVSets; } uint texCoordSet = 2; if ( tt != TechniqueType.LowLod ) { for ( uint i = 0; i < numUVSets; ++i ) { source += "float4 layerUV" + i + " : TEXCOORD" + texCoordSet++ + ", \n"; } } if ( prof.Parent.DebugLevel != 0 && tt != TechniqueType.RenderCompositeMap ) { source += "float2 lodInfo : TEXCOORD" + texCoordSet++ + ", \n"; } source += // Only 1 light supported in this version // deferred shading profile / generator later, ok? :) "uniform float4 ambient,\n" + "uniform float4 lightPosObjSpace,\n" + "uniform float3 lightDiffuseColor,\n" + "uniform float3 lightSpecularColor,\n" + "uniform float3 eyePosObjSpace,\n" + // pack scale, bias and specular "uniform float4 scaleBiasSpecular,\n"; if ( tt == TechniqueType.LowLod ) { // single composite map covers all the others below source += "uniform sampler2D compositeMap : register(s0)\n"; } else { source += "uniform sampler2D globalNormal : register(s0)\n"; uint currentSamplerIdx = 1; if ( terrain.IsGlobalColorMapEnabled && prof.IsGlobalColorMapEnabled ) { source += ", uniform sampler2D globalColorMap : register(s" + currentSamplerIdx++ + ")\n"; } if ( prof.IsLightMapEnabled ) { source += ", uniform sampler2D lightMap : register(s" + currentSamplerIdx++ + ")\n"; } // Blend textures - sampler definitions for ( uint i = 0; i < numBlendTextures; ++i ) { source += ", uniform sampler2D blendTex" + i + " : register(s" + currentSamplerIdx++ + ")\n"; } // Layer textures - sampler definitions & UV multipliers for ( uint i = 0; i < numLayers; ++i ) { source += ", uniform sampler2D difftex" + i + " : register(s" + currentSamplerIdx++ + ")\n"; source += ", uniform sampler2D normtex" + i + " : register(s" + currentSamplerIdx++ + ")\n"; } } source += ") : COLOR\n" + "{\n" + " float4 outputCol;\n" + " float shadow = 1.0;\n" + // base colour " outputCol = float4(0,0,0,1);\n"; if ( tt != TechniqueType.LowLod ) { source += // global normal " float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n"; } source += " float3 lightDir = \n" + " lightPosObjSpace.xyz - (position.xyz * lightPosObjSpace.w);\n" + " float3 eyeDir = eyePosObjSpace - position.xyz;\n" + // set up accumulation areas " float3 diffuse = float3(0,0,0);\n" + " float specular = 0;\n"; if ( tt == TechniqueType.LowLod ) { // we just do a single calculation from composite map source += " float4 composite = tex2D(compositeMap, uv);\n" + " diffuse = composite.rgb;\n"; // TODO - specular; we'll need normals for this! } else { // set up the blend values for ( uint i = 0; i < numBlendTextures; ++i ) { source += " float4 blendTexVal" + i + " = tex2D(blendTex" + i + ", uv);\n"; } if ( prof.IsLayerNormalMappingEnabled ) { // derive the tangent space basis // we do this in the pixel shader because we don't have per-vertex normals // because of the LOD, we use a normal map // tangent is always +x or -z in object space depending on alignment switch ( terrain.Alignment ) { case Alignment.Align_X_Y: case Alignment.Align_X_Z: source += " float3 tangent = float3(1, 0, 0);\n"; break; case Alignment.Align_Y_Z: source += " float3 tangent = float3(0, 0, -1);\n"; break; } source += " float3 binormal = normalize(cross(tangent, normal));\n"; // note, now we need to re-cross to derive tangent again because it wasn't orthonormal source += " tangent = normalize(cross(normal, binormal));\n"; // derive final matrix source += " float3x3 TBN = float3x3(tangent, binormal, normal);\n"; // set up lighting result placeholders for interpolation source += " float4 litRes, litResLayer;\n"; source += " float3 TSlightDir, TSeyeDir, TShalfAngle, TSnormal;\n"; if ( prof.IsLayerParallaxMappingEnabled ) { source += " float displacement;\n"; } // move source += " TSlightDir = normalize(mul(TBN, lightDir));\n"; source += " TSeyeDir = normalize(mul(TBN, eyeDir));\n"; } else { // simple per-pixel lighting with no normal mapping source += " lightDir = normalize(lightDir);\n"; source += " eyeDir = normalize(eyeDir);\n"; source += " float3 halfAngle = normalize(lightDir + eyeDir);\n"; source += " float4 litRes = lit(dot(lightDir, normal), dot(halfAngle, normal), scaleBiasSpecular.z);\n"; } } }