Shaders. The first 4 textures contain height, normal and tile weight information. the next 8 textures are the actual colour textures for the terrain. all textures and samplers are bound to the vertex and pixel shader using the same slots (for compatibility with opengl). The pixel shader contains 1 sampler for each texture but theyre all the same. The vertex shader is probaly pretty irrelevent to the question.
Texture2D tex1 : register( t0 );
SamplerState samp1 : register( s0 );
Texture2D tex2 : register( t1 );
SamplerState samp2 : register( s1 );
Texture2D tex3 : register( t2 );
SamplerState samp3 : register( s2 );
Texture2D tex4 : register( t3 );
SamplerState samp4 : register( s3 );
struct VSInput
{
float4 pos : a_position;
float2 textCoordA : v_textCoordA;
};
struct PSInput
{
float4 pos : SV_Position;
float3 p : v_pos;
float2 textCoordA : v_textCoordA;
float3 normal : v_normal;
float2 gridPos : v_gridPos;
float w1 : v_w1;
float w2 : v_w2;
float w3 : v_w3;
float w4 : v_w4;
float w5 : v_w5;
float w6 : v_w6;
float w7 : v_w7;
float w8 : v_w8;
float2 c : v_c;
};
PSInput main(VSInput attrib)
{
PSInput ps;
float4 vpos = attrib.pos;
ps.gridPos = float2(vpos.x*128,vpos.y*128);
float2 coord = float2(vpos.x/(8*lod)+tOffset.x,vpos.y/(8*lod)+tOffset.y);
ps.c = coord;
coord.x = clamp(coord.x,1.0/1024.0,1023.0/1024.0);
coord.y = clamp(coord.y,1.0/1024.0,1023.0/1024.0);
coord = float2(coord.x+1.0/2048, coord.y+1.0/2048);
float2 ic = float2(vpos.x/(8*lod)+tOffset.x,vpos.y/(8*lod)+tOffset.y);
ic = ic * 1024;
ic.x = clamp(ic.x,1.0,1023.0);
ic.y = clamp(ic.y,1.0,1023.0);
uint3 ici = uint3(ic.x,ic.y,0);
float4 t1 = tex1.Load(ici);
float4 t2 = tex2.Load(ici);
float4 t3 = tex3.Load(ici);
float4 t4 = tex4.Load(ici);
float h = (t1.r-(127.0/255.0)) * 65280.0 +
t1.g * 255.0 +
t1.b * 1.0;
vpos.z = vpos.z + h;
ps.normal = t2.rgb*2-1;
float4 vertPos = mul(vpos,modelMatrix);
ps.pos = mul(vertPos,projectionAndViewMatrix);
ps.p = vertPos.xyz;
ps.w1 = t3.r;
ps.w2 = t3.g;
ps.w3 = t3.b;
ps.w4 = t3.a;
ps.w5 = t4.r;
ps.w6 = t4.g;
ps.w7 = t4.b;
ps.w8 = t4.a;
ps.textCoordA = attrib.textCoordA*(8.0/lod);
return ps;
}
[/source]
Pixel shader
[source]
Texture2D tex1 : register( t0 );
Texture2D tile1 : register( t4 );
Texture2D tile2 : register( t5 );
Texture2D tile3 : register( t6 );
Texture2D tile4 : register( t7 );
Texture2D tile5 : register( t8 );
Texture2D tile6 : register( t9 );
Texture2D tile7 : register( t10 );
Texture2D tile8 : register( t11 );
SamplerState samp1 : register( s4 );
SamplerState samp2 : register( s5 );
SamplerState samp3 : register( s6 );
SamplerState samp4 : register( s7 );
SamplerState samp5 : register( s8 );
SamplerState samp6 : register( s9 );
SamplerState samp7 : register( s10 );
SamplerState samp8 : register( s11 );
struct PSInput
{
float4 pos : SV_Position;
float3 p : v_pos;
float2 textCoordA : v_textCoordA;
float3 normal : v_normal;
float2 gridPos : v_gridPos;
float w1 : v_w1;
float w2 : v_w2;
float w3 : v_w3;
float w4 : v_w4;
float w5 : v_w5;
float w6 : v_w6;
float w7 : v_w7;
float w8 : v_w8;
float2 c : v_c;
};
float4 main(PSInput ps) : SV_Target
{
uint x = int(floor(ps.p.x));
uint y = int(floor(ps.p.y));
x = x % 1024;
y = y % 1024;
float4 t1 = tex1.Load(uint3(ps.c.x*1024,ps.c.y*1024,0));
if (t1.a == 0.0) //indicates a hole in the terrain. Caves etc.
{
discard;
}
float3 n = normalize(ps.normal);
float4 c1 = tile1.Sample(samp1, ps.textCoordA);
float4 c2 = tile2.Sample(samp2, ps.textCoordA);
float4 c3 = tile3.Sample(samp3, ps.textCoordA);
float4 c4 = tile4.Sample(samp4, ps.textCoordA);
float4 c5 = tile5.Sample(samp5, ps.textCoordA);
float4 c6 = tile6.Sample(samp6, ps.textCoordA);
float4 c7 = tile7.Sample(samp7, ps.textCoordA);
float4 c8 = tile8.Sample(samp8, ps.textCoordA);
float4 fvBaseColor = c1;
float total = ps.w1+ps.w2+ps.w3+ps.w4+ps.w5+ps.w6+ps.w7+ps.w8;
if (total > 0.0)
{
fvBaseColor = c1 * ps.w1 + c2 * ps.w2 + c3 * ps.w3 + c4 * ps.w4 + c5 * ps.w5 + c6 * ps.w6 + c7 * ps.w7 + c8 * ps.w8;
fvBaseColor.a = 0.0;
}
float NdotL = max(dot(n, normalize(float3(1000,-1000,1000))),0.0);
float cut = 0.3;
float shade = 0.85;
if (NdotL > cut + 0.05)
{
NdotL = 1.0;
}
else if (NdotL < cut)
{
NdotL = shade;
}
else
{
NdotL = ((NdotL - cut)*20)*(1.0-shade)+shade;
}
NdotL = 1.0 - NdotL;
float4 diffuse = float4(fvBaseColor.r-NdotL,fvBaseColor.g-NdotL,fvBaseColor.b-NdotL,1.0);
float4 colour1 = diffuse;
float len = length(ps.p-camera);
float start = 500.0;
float end = 700.0;
float4 fadeColor = float4(0.3,0.3,0.9,1.0);
if (len > start)
{
if (len > end)
{
colour1 = fadeColor;
}
else
{
colour1 = lerp(colour1,fadeColor,(len-start)/(end-start));
}
}
float4 cc2 = colour1;
cc2.a = 1.0;
if (death == 1)
{
float grey = (cc2.r+cc2.g+cc2.b)/3.0;
cc2.r = grey;
cc2.g = grey;
cc2.b = grey;
}
return cc2;
}
Here is the code that creates the texture. My game engine is written in Java but since you cant do directx in java directly I wrote my directx calls in c++). I create and concatenate all the mip levels in java before calling this c++ method. If the mipLevel is higher than one this method will only work for textures that has width and height values that is multiples of 2 I will improve that as soon as this problem is sorted out. This is probably the part that I'm the most unsure of. especially where I load all the mipmaps into the SUB_RESOURCE_DATA array.
D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
D3D11_TEXTURE2D_DESC sTexDesc;
sTexDesc.Width = width;
sTexDesc.Height = height;
sTexDesc.MipLevels = mipLevel;
sTexDesc.ArraySize = 1;
sTexDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sTexDesc.SampleDesc.Count = 1;
sTexDesc.SampleDesc.Quality = 0;
sTexDesc.Usage = D3D11_USAGE_IMMUTABLE;
sTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
sTexDesc.CPUAccessFlags = 0;
sTexDesc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA *sSubData = new D3D11_SUBRESOURCE_DATA[mipLevel];
int sofar = 0;
for (int i = 0; i < mipLevel; i++)
{
sSubData.pSysMem = (jbyte*)env->GetDirectBufferAddress(data) + sofar;
sSubData.SysMemPitch = (UINT)(width * 4);
sSubData.SysMemSlicePitch = (UINT)(width * height * 4);
sofar += (int)(width * height * 4);
width = width / 2;
height = height / 2;
}
hr = g_pd3dDevice->CreateTexture2D(&sTexDesc, sSubData, &pTexture);
delete[] sSubData;
shaderResourceViewDesc.Format = sTexDesc.Format;
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
shaderResourceViewDesc.Texture2D.MipLevels = mipLevel;
g_pd3dDevice->CreateShaderResourceView(pTexture, &shaderResourceViewDesc, &texture.textureView);
D3D11_SAMPLER_DESC sampDesc;
ZeroMemory(&sampDesc, sizeof(sampDesc));
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
g_pd3dDevice->CreateSamplerState(&sampDesc, &texture.sampler);
}
...
...
...
}
If theres any more information needed please ask.