Urho3D Wiki
Register
Advertisement
Urho quad texture

This method means having four textures in one image like this:

Blender bricks quad

Diffuse, normal, specular and height map (the height map is not used in this material). A similar method could be used to pack three textures (which would be better in this case as only three are used) or even more materials in one image file.

This has two advantages:

There is an issue with the mipmaps then using such an image with four textures like displayed above, it results in ugly stripes like these:

Urho quad stripes

To fix this the shader code needs to be a bit different and the image needs to be like this:

Blender bricks quad-0

Each of the four tiles is still used as a square which means that half of the texture (the left part of each rectangle) isn't even used. But it's correcting the mipmaps to not create the black stripes seen above. The material file:

<material>
<technique name="Techniques/quad.xml"/>
<texture name="Textures/blender_bricks_quad.jpg" unit="diffuse"/>
<parameter name="MatDiffColor" value="1.0 1.0 1.0 1"/>
<parameter name="MatSpecColor" value="2.5 2.5 2.5 30"/>
</material>

unit="diffuse" has no real meaning here as this image is not only used for diffuse. This just says that this texture is number 0 which is important for the shader as it expects the one texture at "place" 0.

The technique: "Techniques/quad.xml"

<technique vs="quad" ps="quad">
<pass name="base" />
<pass name="litbase" psdefines="AMBIENT" />
<pass name="light" depthtest="equal" depthwrite="false" blend="add" />
<pass name="prepass" psdefines="PREPASS" />
<pass name="material" psdefines="MATERIAL" depthtest="equal" depthwrite="false" />
<pass name="deferred" psdefines="DEFERRED" />
<pass name="depth" vs="Depth" ps="Depth" />
<pass name="shadow" vs="Shadow" ps="Shadow" />
</technique>

The GLSL shader (based on the terrain shader and parts from LitBase): "Shaders/quad.glsl"

#include "Uniforms.glsl"
#include "Samplers.glsl"
#include "Transform.glsl"
#include "ScreenPos.glsl"
#include "Lighting.glsl"
#include "Fog.glsl"

varying vec4 vTexCoord;

varying vec3 vNormal;
varying vec4 vTangent;
varying vec4 vWorldPos;
#ifdef PERPIXEL
#ifdef SHADOW
varying vec4 vShadowPos[NUMCASCADES];
#endif
#ifdef SPOTLIGHT
varying vec4 vSpotPos;
#endif
#ifdef POINTLIGHT
varying vec3 vCubeMaskVec;
#endif
#else
varying vec3 vVertexLight;
varying vec4 vScreenPos;
#ifdef ENVCUBEMAP
varying vec3 vReflectionVec;
#endif
#if defined(LIGHTMAP) || defined(AO)
varying vec2 vTexCoord2;
#endif
#endif

uniform sampler2D sTexture0;

void VS()
{
mat4 modelMatrix = iModelMatrix;
vec3 worldPos = GetWorldPos(modelMatrix);
gl_Position = GetClipPos(worldPos);
vNormal = GetWorldNormal(modelMatrix);
vWorldPos = vec4(worldPos, GetDepth(gl_Position));
vec3 tangent = GetWorldTangent(modelMatrix);
vec3 bitangent = cross(tangent, vNormal) * iTangent.w;
vTexCoord = vec4(GetTexCoord(iTexCoord), bitangent.xy);
vTangent = vec4(tangent, bitangent.z);
#ifdef PERPIXEL
// Per-pixel forward lighting
vec4 projWorldPos = vec4(worldPos, 1.0);

#ifdef SHADOW
// Shadow projection: transform from world space to shadow space
for (int i = 0; i < NUMCASCADES; i++)
vShadowPos[i] = GetShadowPos(i, projWorldPos);
#endif
#ifdef SPOTLIGHT
// Spotlight projection: transform from world space to projector texture coordinates
vSpotPos = projWorldPos * cLightMatrices[0];
#endif

#ifdef POINTLIGHT
vCubeMaskVec = (worldPos - cLightPos.xyz) * mat3(cLightMatrices[0][0].xyz, cLightMatrices[0][1].xyz, cLightMatrices[0][2].xyz);
#endif
#else
// Ambient & per-vertex lighting
#if defined(LIGHTMAP) || defined(AO)
// If using lightmap, disregard zone ambient light
// If using AO, calculate ambient in the PS
vVertexLight = vec3(0.0, 0.0, 0.0);
vTexCoord2 = iTexCoord2;
#else
vVertexLight = GetAmbient(GetZonePos(worldPos));
#endif
#ifdef NUMVERTEXLIGHTS
for (int i = 0; i < NUMVERTEXLIGHTS; ++i)
vVertexLight += GetVertexLight(i, worldPos, vNormal) * cVertexLights[i * 3].rgb;
#endif
vScreenPos = GetScreenPos(gl_Position);
#ifdef ENVCUBEMAP
vReflectionVec = worldPos - cCameraPos;
#endif #endif
}

void PS()
{ vec2 tex_coord_diff=vec2(mod(vTexCoord.x/4.0,0.25),vTexCoord.y);
tex_coord_diff.x=0.0625+tex_coord_diff.x*0.5;
vec4 diffColor=cMatDiffColor.rgba*texture2D(sTexture0,tex_coord_diff);

// Get material specular albedo
vec3 specColor = cMatSpecColor.rgb*texture2D(sTexture0,vec2(tex_coord_diff.x+0.5,tex_coord_diff.y)).rgb;

// Get normal
mat3 tbn = mat3(vTangent.xyz, vec3(vTexCoord.zw, vTangent.w), vNormal);
vec3 normal = normalize(tbn * DecodeNormal(texture2D(sTexture0,vec2(tex_coord_diff.x+0.25,tex_coord_diff.y))));

// Get fog factor
#ifdef HEIGHTFOG
float fogFactor = GetHeightFogFactor(vWorldPos.w, vWorldPos.y);
#else
float fogFactor = GetFogFactor(vWorldPos.w);
#endif

#if defined(PERPIXEL)
// Per-pixel forward lighting
vec3 lightColor;
vec3 lightDir;
vec3 finalColor;
float diff = GetDiffuse(normal, vWorldPos.xyz, lightDir);
#ifdef SHADOW
diff *= GetShadow(vShadowPos, vWorldPos.w);
#endif
#if defined(SPOTLIGHT)
lightColor = vSpotPos.w > 0.0 ? texture2DProj(sLightSpotMap, vSpotPos).rgb * cLightColor.rgb : vec3(0.0, 0.0, 0.0);
#elif defined(CUBEMASK)
lightColor = textureCube(sLightCubeMap, vCubeMaskVec).rgb * cLightColor.rgb;
#else
lightColor = cLightColor.rgb;
#endif

#ifdef SPECULAR
float spec = GetSpecular(normal, cCameraPosPS - vWorldPos.xyz, lightDir, cMatSpecColor.a);
finalColor = diff * lightColor * (diffColor.rgb + spec * specColor * cLightColor.a);
#else
finalColor = diff * lightColor * diffColor.rgb;
#endif

#ifdef AMBIENT
finalColor += cAmbientColor * diffColor.rgb;
finalColor += cMatEmissiveColor;
gl_FragColor = vec4(GetFog(finalColor, fogFactor), diffColor.a);
#else
gl_FragColor = vec4(GetLitFog(finalColor, fogFactor), diffColor.a);
#endif
#elif defined(PREPASS)
// Fill light pre-pass G-Buffer
float specPower = cMatSpecColor.a / 255.0;

gl_FragData[0] = vec4(normal * 0.5 + 0.5, specPower);
gl_FragData[1] = vec4(EncodeDepth(vWorldPos.w), 0.0);
#elif defined(DEFERRED)
// Fill deferred G-buffer
float specIntensity = specColor.g;
float specPower = cMatSpecColor.a / 255.0;

gl_FragData[0] = vec4(GetFog(vVertexLight * diffColor.rgb, fogFactor), 1.0);
gl_FragData[1] = fogFactor * vec4(diffColor.rgb, specIntensity);
gl_FragData[2] = vec4(normal * 0.5 + 0.5, specPower);
gl_FragData[3] = vec4(EncodeDepth(vWorldPos.w), 0.0);
#else
// Ambient & per-vertex lighting
vec3 finalColor = vVertexLight * diffColor.rgb;

#ifdef MATERIAL
// Add light pre-pass accumulation result
// Lights are accumulated at half intensity. Bring back to full intensity now
vec4 lightInput = 2.0 * texture2DProj(sLightBuffer, vScreenPos);
vec3 lightSpecColor = lightInput.a * lightInput.rgb / max(GetIntensity(lightInput.rgb), 0.001);

finalColor += lightInput.rgb * diffColor.rgb + lightSpecColor * specColor;
#endif
gl_FragColor = vec4(GetFog(finalColor, fogFactor), diffColor.a);
#endif
}

See also Terrain Shader with normal, specular and height mapping.

Another note on the texture sampling issue: Theoretically this can be also fixed by doing manual texture sampling in the shader by using texelFetch instead of texture2D, but it was super slow in my tests. My FPS in FullHD dropped from >200 to ~40.

Advertisement