From 480ade634ddab96fc3a07d24ec5bf5bdd1926a72 Mon Sep 17 00:00:00 2001 From: Peter Wortmann Date: Sun, 20 Sep 2015 14:50:22 +0100 Subject: [PATCH] Shiny materials, shader reorganisation This implements the proposal made in the forum for "shiny" materials - material can now determine the angle at which the most light is reflected. Shiny materials might set this lower to approximate a "reflection" effect, and increase the "spottiness" at the same time. To compensate for the lack of brightness without light, "emittance" can be used. Not sure this is the most elegant way to model this - the "proper" way here would be to have emittance, shading and specular as three separate light parameters instead of molding one into the other and using the third to compensate. Furthermore, this reorganises shaders in a major way: We reduce the number of shader files down to three, pushing a number of possible configurations into preprocessor. I believe this should be easier to understand, which for the moment trumps theoretical extensibility benefits. --- planet/Graphics.ocg/AmbientShader.glsl | 40 ----- planet/Graphics.ocg/CommonShader.glsl | 162 +++++++++++++++++++ planet/Graphics.ocg/GammaShader.glsl | 7 - planet/Graphics.ocg/LandscapeShader.glsl | 82 ++++++---- planet/Graphics.ocg/LightShader.glsl | 99 ------------ planet/Graphics.ocg/ObjectBaseShader.glsl | 35 ---- planet/Graphics.ocg/ObjectLightShader.glsl | 43 ----- planet/Graphics.ocg/ObjectShader.glsl | 109 +++++++++++++ planet/Graphics.ocg/ScalerShader.glsl | 49 ------ planet/Graphics.ocg/SpriteOverlayShader.glsl | 11 -- planet/Graphics.ocg/SpriteTextureShader.glsl | 6 - planet/Graphics.ocg/UtilShader.glsl | 17 -- planet/Material.ocg/Gold.ocm | 5 +- src/graphics/C4DrawGL.cpp | 19 +-- src/landscape/C4LandscapeRender.cpp | 49 +++--- src/landscape/C4LandscapeRender.h | 3 +- src/landscape/C4Material.cpp | 11 +- src/landscape/C4Material.h | 4 + src/lib/StdMeshMaterial.cpp | 1 - src/object/C4Def.cpp | 26 +-- 20 files changed, 380 insertions(+), 398 deletions(-) delete mode 100644 planet/Graphics.ocg/AmbientShader.glsl create mode 100644 planet/Graphics.ocg/CommonShader.glsl delete mode 100644 planet/Graphics.ocg/GammaShader.glsl delete mode 100644 planet/Graphics.ocg/LightShader.glsl delete mode 100644 planet/Graphics.ocg/ObjectBaseShader.glsl delete mode 100644 planet/Graphics.ocg/ObjectLightShader.glsl create mode 100644 planet/Graphics.ocg/ObjectShader.glsl delete mode 100644 planet/Graphics.ocg/ScalerShader.glsl delete mode 100644 planet/Graphics.ocg/SpriteOverlayShader.glsl delete mode 100644 planet/Graphics.ocg/SpriteTextureShader.glsl delete mode 100644 planet/Graphics.ocg/UtilShader.glsl diff --git a/planet/Graphics.ocg/AmbientShader.glsl b/planet/Graphics.ocg/AmbientShader.glsl deleted file mode 100644 index 3fe12fc1b..000000000 --- a/planet/Graphics.ocg/AmbientShader.glsl +++ /dev/null @@ -1,40 +0,0 @@ - -// Ambient light calculation - -#ifdef HAVE_LIGHT -uniform sampler2D ambientTex; - -uniform mat3x2 ambientTransform; -uniform float ambientBrightness; -#endif -//uniform float cullMode; // 0 if backface culling is enabled, 1 if it is disabled -// Already declared in LightShader.glsl - -slice(texture+6) -{ -#ifdef HAVE_LIGHT - // Ambient light - // Extra .xy since some old intel drivers return a vec3 - float ambient = texture2D(ambientTex, (ambientTransform * vec3(gl_FragCoord.xy, 1.0)).xy).r * ambientBrightness; -#else - // Lighting disabled: Ambient light everywhere - float ambient = 1.0; -#endif -} - -slice(light+1) -{ - // Add ambience to brightness -#ifdef LANDSCAPE - // For landscape, ambient brightness is coming from top - vec3 ambientDir = vec3(0.0, -1.0, 0.0); - light = mix(light, 1.0 + 1.0 * dot(normal, ambientDir), ambient); -#ifdef HAVE_2PX - light2 = mix(light2, 1.0 + 1.0 * dot(normal2, ambientDir), ambient); -#endif -#else - // For objects, ambient brightness is coming from the front - vec3 ambientDir = vec3(0.0, 0.0, 1.0); - light = mix(light, max(max(dot(normal, ambientDir), 0.0), cullMode * max(dot(-normal, ambientDir), 0.0)), ambient); -#endif -} diff --git a/planet/Graphics.ocg/CommonShader.glsl b/planet/Graphics.ocg/CommonShader.glsl new file mode 100644 index 000000000..bddaf91ea --- /dev/null +++ b/planet/Graphics.ocg/CommonShader.glsl @@ -0,0 +1,162 @@ + +// uncomment the following lines for debugging light directions: +// yellow: light up, blue: light down, turqoise: light right, pink: light left +// brightness: light strength +//#define LIGHT_DEBUG + +// uncomment the following lines for debugging light color: +// the light will always come from the front and have a uniform brightness. +//#define LIGHT_DEBUG_COLOR + +// uncomment the following lines to set the light color to pink for all lights for debugging: +//#define LIGHT_DEBUG_PINK + +// At what point of light intensity we set the "darkness" point. This +// is to compensate for the fact that the engine "smoothes" the light +// and therefore will often never arrive at 0 light intensity. +const float lightDarknessLevel = 8.0 / 256.0; + +#ifdef HAVE_LIGHT +uniform sampler2D ambientTex; + +uniform mat3x2 ambientTransform; +uniform float ambientBrightness; + +uniform sampler2D lightTex; +#endif + +// Gamma uniforms +uniform vec3 gamma; + +// 0 if backface culling is enabled, 1 if it is disabled +uniform float cullMode; + +// Light dot product, taking cull mode into account +float lightDot(vec3 normal, vec3 lightDir) { + return abs(max(-cullMode, dot(normalize(normal), lightDir))); +} + +vec3 extend_normal(vec2 v) +{ + // the higher the second value, the further away the light source from the landscape + return normalize(vec3(v, 0.45)); +} + +// Converts the pixel range 0.0..1.0 into the integer range 0..255 +int f2i(float x) { + return int(x * 255.9); +} + +slice(texture+5) +{ +#ifdef HAVE_LIGHT + + // Query light texture + vec2 lightDirCoord = lightCoord.st; + + vec4 lightPx = texture2D(lightTex, lightDirCoord); + float lightBright = 2.0*max(0.0, lightPx.a-lightDarknessLevel); + vec3 lightDir = extend_normal(vec2(1.0, 1.0) - lightPx.yz * 3.0); + + // Query light color texture (part of the light texture) + vec2 lightColorCoord = lightCoord.st - vec2(0.0, 0.5); // subtract offset for the color texture + + vec3 lightColor = texture2D(lightTex, lightColorCoord.st).rgb; + + // Normalise light colour + #ifdef LIGHT_DEBUG_COLOR + lightBright = 0.5; + lightColor = vec4(1.0, 0.0, 1.0, 1.0); + #endif +#else + // When lighting is disabled, put a light source coming from the camera. + // Note that in most cases this does not actually matter, since in the + // case with lighting disabled, ambient lighting takes fully over. + float lightBright = 0.5; + vec3 lightDir = vec3(0.0, 0.0, 1.0); + vec3 lightColor = vec3(1.0, 1.0, 1.0); +#endif + +#ifdef HAVE_LIGHT + // Ambient light + // Extra .xy since some old intel drivers return a vec3 + float ambient = texture2D(ambientTex, (ambientTransform * vec3(gl_FragCoord.xy, 1.0)).xy).r * ambientBrightness; +#else + // Lighting disabled: Ambient light everywhere + float ambient = 1.0; +#endif +} + +slice(light) +{ + + // Light dot product, taking backface culling into account + normal = normalize(normal); + float light = lightDot(normal, lightDir); + // Amount of reflection, depending on the angle where material reflects most + light = min(light / matAngle, 2.0 - light / matAngle); + +#ifdef LANDSCAPE + normal2 = normalize(normal2); + float light2 = lightDot(normal2, lightDir); + light2 = min(light2 / matAngle2, 2.0 - light2 / matAngle2); +#endif +} + +slice(light+1) +{ +#ifdef LANDSCAPE + // For landscape, ambient brightness is coming from top + vec3 ambientDir = normalize(vec3(0.0, -1.0, 1.0)); +#else + // For objects, ambient brightness is coming from the front + vec3 ambientDir = vec3(0.0, 0.0, 1.0); +#endif + // Add ambience to brightness + lightBright = mix(lightBright, 1.0, ambient); + light = mix(light, 0.5 * (ambient + lightDot(normal, ambientDir)), ambient); +#ifdef LANDSCAPE + light2 = mix(light2, 0.5 * (ambient + lightDot(normal2, ambientDir)), ambient); +#endif + lightColor = mix(lightColor, vec3(1.0,1.0,1.0), ambient); +} + +slice(color+5) +{ + // Normalize light colour + vec3 lightColorNorm = sqrt(3.0) * normalize(lightColor); + + // Add light. Depending on material properties, we make it more + // "spotty" and allow the material to self-illuminate. The light + // brightness overrules everything though (= FoW is last factor). + vec3 spotLight = pow(vec3(light,light,light), matSpot); + color.rgb = lightBright * color.rgb * (matEmit + lightColorNorm * spotLight); +#ifdef LANDSCAPE + vec3 spotLight2 = pow(vec3(light2,light2,light2), matSpot2); + color2.rgb = lightBright * color2.rgb * (matEmit2 + lightColorNorm * spotLight2); +#endif +} + +slice(finish+5) +{ + +#ifdef LIGHT_DEBUG +#ifdef HAVE_LIGHT + float lightYDir = lightPx.b - 1.0/3.0; + float lightXDir = lightPx.g - 1.0/3.0; + float lightStrength = lightPx.a; + color = + vec4(lightStrength * vec3(1.0-1.5*(max(0.0, lightYDir) + max(0.0,lightXDir)), + 1.0-1.5*(max(0.0, lightYDir) + max(0.0,-lightXDir)), + 1.0-1.5*max(0.0, -lightYDir)), + 1.0); +#else + color = vec4(0.0, 0.0, 0.0, 0.0); // invisible +#endif +#endif + +} + +slice(finish+10) { + color = vec4(pow(color.rgb, gamma), color.a); +} diff --git a/planet/Graphics.ocg/GammaShader.glsl b/planet/Graphics.ocg/GammaShader.glsl deleted file mode 100644 index af81d0d7a..000000000 --- a/planet/Graphics.ocg/GammaShader.glsl +++ /dev/null @@ -1,7 +0,0 @@ - -// Gamma uniforms -uniform vec3 gamma; - -slice(finish+10) { - color = vec4(pow(color.rgb, gamma), color.a); -} diff --git a/planet/Graphics.ocg/LandscapeShader.glsl b/planet/Graphics.ocg/LandscapeShader.glsl index ea370d74a..13a75cd5c 100644 --- a/planet/Graphics.ocg/LandscapeShader.glsl +++ b/planet/Graphics.ocg/LandscapeShader.glsl @@ -1,5 +1,4 @@ - // Input textures uniform sampler2D landscapeTex[2]; uniform sampler2D scalerTex; @@ -11,11 +10,7 @@ uniform vec2 resolution; // Center position uniform vec2 center; // Texture map -#ifndef NO_BROKEN_ARRAYS_WORKAROUND uniform sampler1D matMapTex; -#else -uniform float matMap[256]; -#endif uniform int materialDepth; uniform vec2 materialSize; @@ -25,20 +20,15 @@ const vec2 scalerStepY = vec2(0.0, 1.0 / 32.0); const vec2 scalerOffset = scalerStepX / 3.0 + scalerStepY / 3.0; const vec2 scalerPixel = vec2(scalerStepX.x, scalerStepY.y) / 3.0; -// Parameters +// How much % the normals from the normal map are added up to the +// landscape normal. The higher the strength, the more structure +// within the material is visible but also the less the borders +// between the different materials stand out. +const float normalMapStrength = 0.20; -// how much % the normals from the normal map are added up to the landscape normal. The higher the strength, the more -// structure within the material is visible but also the less the borders between the different materials stand out. -const float normalMapStrength = 0.75; - -float queryMatMap(int pix) +vec4 queryMatMap(int pix) { -#ifndef NO_BROKEN_ARRAYS_WORKAROUND - int idx = f2i(texture1D(matMapTex, float(pix) / 256.0 + 0.5 / 256.0).r); - return float(idx) / 256.0 + 0.5 / float(materialDepth); -#else - return matMap[pix]; -#endif + return texture1D(matMapTex, float(pix) / 2.0 / 256.0 + 0.5 / 2.0 / 256.0); } slice(coordinate) @@ -63,44 +53,74 @@ slice(texture) // our pixel color (without/with interpolation) vec4 landscapePx = texture2D(landscapeTex[0], centerCoo); vec4 realLandscapePx = texture2D(landscapeTex[0], texCoo); + + // find scaler coordinate + vec2 scalerCoo = scalerOffset + mod(pixelCoo, vec2(1.0, 1.0)) * scalerPixel; + int iScaler = f2i(landscapePx.a), iRow = iScaler / 8; + scalerCoo.x += float(iScaler - iRow * 8) / 8.0; + scalerCoo.y += float(iScaler / 8) / 32.0; + + // query scaler texture + vec4 scalerPx = texture2D(scalerTex, scalerCoo); + + // Get "second" landscape pixel + vec2 centerCoo2 = centerCoo + fullStep * floor(vec2(-0.5, -0.5) + + scalerPx.gb * 255.0 / 64.0); + vec4 landscapePx2 = texture2D(landscapeTex[0], centerCoo2); + } slice(material) { - // Get material pixels - float materialIx = queryMatMap(f2i(landscapePx.r)); + // Get material properties from material map + int matMapIx = f2i(landscapePx.r); + vec4 matMap = queryMatMap(2*matMapIx); + vec4 matMapX = queryMatMap(2*matMapIx+1); + float materialIx = float(f2i(matMap.a)) / 256.0 + 0.5 / float(materialDepth); + vec3 matEmit = matMap.rgb; + vec3 matSpot = matMapX.rgb * 255.9f / 16.0f; + float matAngle = matMapX.a; + + // Query material texture pixels vec4 materialPx = texture3D(materialTex, vec3(materialCoo, materialIx)); vec4 normalPx = texture3D(materialTex, vec3(materialCoo, materialIx+0.5)); - // Same for second pixel, but we'll simply use the first normal -#ifdef HAVE_2PX - float materialIx2 = queryMatMap(f2i(landscapePx2.r)); + // Same for second pixel + int matMapIx2 = f2i(landscapePx2.r); + vec4 matMap2 = queryMatMap(2*matMapIx2); + vec4 matMapX2 = queryMatMap(2*matMapIx2+1); + float materialIx2 = float(f2i(matMap2.a)) / 256.0 + 0.5 / float(materialDepth); + vec3 matEmit2 = matMap2.rgb; + vec3 matSpot2 = matMapX2.rgb * 255.9f / 16.0f; + float matAngle2 = matMapX2.a; + + // Query material texture pixels vec4 materialPx2 = texture3D(materialTex, vec3(materialCoo, materialIx2)); vec4 normalPx2 = texture3D(materialTex, vec3(materialCoo, materialIx2+0.5)); -#endif } slice(normal) { // Normal calculation - vec3 normal = extend_normal(mix(realLandscapePx.yz, landscapePx.yz, scalerPx.a) - - vec2(0.5, 0.5)); + vec3 normal = extend_normal(1.5 * (mix(realLandscapePx.yz, landscapePx.yz, scalerPx.a) + - vec2(0.5, 0.5))); vec3 textureNormal = normalPx.xyz - vec3(0.5,0.5,0.5); - normal = normal + textureNormal * normalMapStrength; + normal = mix(textureNormal, normal, normalMapStrength); -#ifdef HAVE_2PX vec3 normal2 = extend_normal(landscapePx2.yz - vec2(0.5, 0.5)); vec3 textureNormal2 = normalPx2.xyz - vec3(0.5,0.5,0.5); - normal2 = normal2 + textureNormal2 * normalMapStrength; -#endif + normal2 = mix(textureNormal2, normal2, normalMapStrength); } slice(color) { #define color gl_FragColor color = materialPx; -#ifdef HAVE_2PX vec4 color2 = materialPx2; -#endif +} + +slice(color+10) { + // Mix second color into main color according to scaler + color = mix(color2, color, scalerPx.r); } diff --git a/planet/Graphics.ocg/LightShader.glsl b/planet/Graphics.ocg/LightShader.glsl deleted file mode 100644 index 985e8a527..000000000 --- a/planet/Graphics.ocg/LightShader.glsl +++ /dev/null @@ -1,99 +0,0 @@ - -// Base light calculations - -#ifdef HAVE_LIGHT -uniform sampler2D lightTex; -#endif -uniform float cullMode; // 0 if backface culling is enabled, 1 if it is disabled - -// uncomment the following lines for debugging light directions: -// yellow: light up, blue: light down, turqoise: light right, pink: light left -// brightness: light strength -//#define LIGHT_DEBUG - -// uncomment the following lines for debugging light color: -// the light will always come from the front and have a uniform brightness. -//#define LIGHT_DEBUG_COLOR - -// uncomment the following lines to set the light color to pink for all lights for debugging: -//#define LIGHT_DEBUG_PINK - -// At what point of light intensity we set the "darkness" point. This -// is to compensate for the fact that the engine "smoothes" the light -// and therefore will often never arrive at 0 light intensity. -const float lightDarknessLevel = 8.0 / 256.0; - -slice(texture+5) -{ -#ifdef HAVE_LIGHT - - // Query light texture - vec2 lightDirCoord = lightCoord.st; - - vec4 lightPx = texture2D(lightTex, lightDirCoord); - float lightBright = max(0.0, lightPx.a-lightDarknessLevel); - vec3 lightDir = extend_normal(vec2(1.0, 1.0) - lightPx.yz * 3.0); - - // Query light color texture (part of the light texture) - vec2 lightColorCoord = lightCoord.st - vec2(0.0, 0.5); // subtract offset for the color texture - - vec4 lightColor = texture2D(lightTex, lightColorCoord.st); - - #ifdef LIGHT_DEBUG_COLOR - lightBright = 0.5; - lightDir = vec3(0.0, 0.0, 1.0); - #endif -#else - // When lighting is disabled, put a light source coming from the camera. - // Note that in most cases this does not actually matter, since in the - // case with lighting disabled, ambient lighting takes fully over. - float lightBright = 0.5; - vec3 lightDir = vec3(0.0, 0.0, 1.0); - - vec4 lightColor = vec4(1.0, 1.0, 1.0, 1.0); -#endif -} - -slice(light) -{ - float light = 2.0 * lightBright * max(max(dot(normal, lightDir), 0.0), cullMode * max(dot(-normal, lightDir), 0.0)); -#ifdef HAVE_2PX - float light2 = 2.0 * lightBright * max(max(dot(normal2, lightDir), 0.0), cullMode * max(dot(-normal2, lightDir), 0.0)); -#endif -} - -slice(color+5) -{ - // pink shade for debugging! - #ifdef LIGHT_DEBUG_PINK - lightColor = vec4(1.0, 0.0, 1.0, 1.0); - #endif - - lightColor.rgb = sqrt(3.0) * normalize(lightColor.rgb); - - // Add light - color.rgb = light * color.rgb * lightColor.rgb; -#ifdef HAVE_2PX - color2.rgb = light2 * color2.rgb * lightColor.rgb; -#endif -} - -slice(finish+5) -{ - -#ifdef LIGHT_DEBUG -#ifdef HAVE_LIGHT - float lightYDir = lightPx.b - 1.0/3.0; - float lightXDir = lightPx.g - 1.0/3.0; - float lightStrength = lightPx.a; - color = - vec4(lightStrength * vec3(1.0-1.5*(max(0.0, lightYDir) + max(0.0,lightXDir)), - 1.0-1.5*(max(0.0, lightYDir) + max(0.0,-lightXDir)), - 1.0-1.5*max(0.0, -lightYDir)), - 1.0); -#else - color = vec4(0.0, 0.0, 0.0, 0.0); // invisible -#endif -#endif - -} diff --git a/planet/Graphics.ocg/ObjectBaseShader.glsl b/planet/Graphics.ocg/ObjectBaseShader.glsl deleted file mode 100644 index cf9b7b376..000000000 --- a/planet/Graphics.ocg/ObjectBaseShader.glsl +++ /dev/null @@ -1,35 +0,0 @@ -uniform vec4 clrMod; - -slice(init) -{ -#define color gl_FragColor - -#ifdef MESH - // TODO: Add emission part of the material. Note we cannot just - // add this to the color, but it would need to be handled separately, - // such that it is independent from the incident light direction. - color = gl_FrontMaterial.diffuse; -#else - vec4 baseColor = gl_Color; - color = baseColor; -#endif - -} - -slice(color) -{ - // TODO: Instead of a conditional, we could compute the color as - // color = A + B*color + C*clrMod + D*color*clrMod; - // Then, mod2 would be A=-0.5, B=1, C=1, D=0 - // and default would be A=0,B=0,C=0,D=1 - // Would allow to avoid conditionsals and #ifdefs, but need 4 uniforms... - - // Could also try some sort of 3x3 matrix: - // out = (color, clrmod, 1) * (A,B,C,D,E,F,0,0,G) * (color, clrmod, 1) - -#ifdef CLRMOD_MOD2 - color = clamp(color + clrMod - 0.5, 0.0, 1.0); -#else - color = color * clrMod; -#endif -} diff --git a/planet/Graphics.ocg/ObjectLightShader.glsl b/planet/Graphics.ocg/ObjectLightShader.glsl deleted file mode 100644 index 5487a0668..000000000 --- a/planet/Graphics.ocg/ObjectLightShader.glsl +++ /dev/null @@ -1,43 +0,0 @@ -uniform mat3x2 lightTransform; -#ifdef HAVE_NORMALMAP -uniform sampler2D normalTex; -#endif - -#ifdef MESH -varying vec3 normalDir; -#endif - -slice(texture+4) -{ -#ifdef HAVE_LIGHT - // prepare texture coordinate for light lookup in LightShader.c - // Extra .xy since some old intel drivers return a vec3 - vec2 lightCoord = (lightTransform * vec3(gl_FragCoord.xy, 1.0)).xy; -#endif -} - -slice(normal) -{ -#ifdef HAVE_NORMALMAP - vec4 normalPx = texture2D(normalTex, texcoord.xy); - vec3 normalPxDir = 2.0 * (normalPx.xyz - vec3(0.5, 0.5, 0.5)); -#ifdef MESH - // For meshes, the normal matrix is typically provided in Clonk - // coordinates, but the normal matrix incorporates a component that - // transforms from Ogre to Clonk coordinates. Therefore, we need to - // reverse that transformation for meshes. - // TODO: This could be optimized since the matrix is so simple that - // we don't need to do a full matrix multiplication. - mat3 c2o = mat3(0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0); - vec3 normal = normalize(c2o * gl_NormalMatrix * normalPxDir); -#else - vec3 normal = normalize(gl_NormalMatrix * normalPxDir); -#endif -#else -#ifdef MESH - vec3 normal = normalDir; // Normal matrix is already applied in vertex shader -#else - vec3 normal = vec3(0.0, 0.0, 1.0); -#endif -#endif -} diff --git a/planet/Graphics.ocg/ObjectShader.glsl b/planet/Graphics.ocg/ObjectShader.glsl new file mode 100644 index 000000000..506fe2c1f --- /dev/null +++ b/planet/Graphics.ocg/ObjectShader.glsl @@ -0,0 +1,109 @@ + +uniform mat3x2 lightTransform; + +#ifdef HAVE_NORMALMAP +uniform sampler2D normalTex; +#endif + +#ifdef MESH +varying vec3 normalDir; +#endif + +uniform vec4 clrMod; + +#ifdef HAVE_BASE +uniform sampler2D baseTex; +#endif + +#ifdef HAVE_OVERLAY +uniform vec4 overlayClr; +uniform sampler2D overlayTex; +#endif + +slice(material) +{ + // Default material properties. TODO: Populate them? + vec3 matEmit = vec3(0.0,0.0,0.0); + vec3 matSpot = vec3(1.0,1.0,1.0); + float matAngle = 1.0; +} + +slice(texture) +{ +#define color gl_FragColor + +#ifdef MESH + // TODO: Add emission part of the material. Note we cannot just + // add this to the color, but it would need to be handled separately, + // such that it is independent from the incident light direction. + color = gl_FrontMaterial.diffuse; +#else + vec4 baseColor = gl_Color; + color = baseColor; +#endif + +#ifdef HAVE_BASE + color = baseColor * texture2D(baseTex, texcoord.xy); +#endif + +#ifdef HAVE_OVERLAY + // Get overlay color from overlay texture + vec4 overlay = baseColor * overlayClr * texture2D(overlayTex, texcoord.xy); + // Mix overlay with texture + float alpha0 = 1.0 - (1.0 - color.a) * (1.0 - overlay.a); + color = vec4(mix(color.rgb, overlay.rgb, overlay.a / alpha0), alpha0); +#endif +} + +slice(texture+4) +{ +#ifdef HAVE_LIGHT + // prepare texture coordinate for light lookup in LightShader.c + // Extra .xy since some old intel drivers return a vec3 + vec2 lightCoord = (lightTransform * vec3(gl_FragCoord.xy, 1.0)).xy; +#endif +} + +slice(normal) +{ +#ifdef HAVE_NORMALMAP + vec4 normalPx = texture2D(normalTex, texcoord.xy); + vec3 normalPxDir = 2.0 * (normalPx.xyz - vec3(0.5, 0.5, 0.5)); +#ifdef MESH + // For meshes, the normal matrix is typically provided in Clonk + // coordinates, but the normal matrix incorporates a component that + // transforms from Ogre to Clonk coordinates. Therefore, we need to + // reverse that transformation for meshes. + // TODO: This could be optimized since the matrix is so simple that + // we don't need to do a full matrix multiplication. + mat3 c2o = mat3(0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0); + vec3 normal = normalize(c2o * gl_NormalMatrix * normalPxDir); +#else + vec3 normal = normalize(gl_NormalMatrix * normalPxDir); +#endif +#else +#ifdef MESH + vec3 normal = normalDir; // Normal matrix is already applied in vertex shader +#else + vec3 normal = vec3(0.0, 0.0, 1.0); +#endif +#endif +} + +slice(color) +{ + // TODO: Instead of a conditional, we could compute the color as + // color = A + B*color + C*clrMod + D*color*clrMod; + // Then, mod2 would be A=-0.5, B=1, C=1, D=0 + // and default would be A=0,B=0,C=0,D=1 + // Would allow to avoid conditionsals and #ifdefs, but need 4 uniforms... + + // Could also try some sort of 3x3 matrix: + // out = (color, clrmod, 1) * (A,B,C,D,E,F,0,0,G) * (color, clrmod, 1) + +#ifdef HAVE_MOD2 + color = clamp(color + clrMod - 0.5, 0.0, 1.0); +#else + color = color * clrMod; +#endif +} diff --git a/planet/Graphics.ocg/ScalerShader.glsl b/planet/Graphics.ocg/ScalerShader.glsl deleted file mode 100644 index a338e3886..000000000 --- a/planet/Graphics.ocg/ScalerShader.glsl +++ /dev/null @@ -1,49 +0,0 @@ - -#define HAVE_2PX - -slice(texture+5) -{ - // find scaler coordinate - vec2 scalerCoo = scalerOffset + mod(pixelCoo, vec2(1.0, 1.0)) * scalerPixel; - -#ifdef SCALER_IN_GPU - if(texture2D(landscapeTex[0], centerCoo - fullStepX - fullStepY).r == landscapePx.r) - scalerCoo += scalerStepX; - if(texture2D(landscapeTex[0], centerCoo - fullStepY).r == landscapePx.r) - scalerCoo += 2.0 * scalerStepX; - if(texture2D(landscapeTex[0], centerCoo + fullStepX - fullStepY).r == landscapePx.r) - scalerCoo += 4.0 * scalerStepX; - - if(texture2D(landscapeTex[0], centerCoo - fullStepX ).r == landscapePx.r) - scalerCoo += scalerStepY; - if(texture2D(landscapeTex[0], centerCoo + fullStepX ).r == landscapePx.r) - scalerCoo += 2.0 * scalerStepY; - - if(texture2D(landscapeTex[0], centerCoo - fullStepX + fullStepY).r == landscapePx.r) - scalerCoo += 4.0 * scalerStepY; - if(texture2D(landscapeTex[0], centerCoo + fullStepY).r == landscapePx.r) - scalerCoo += 8.0 * scalerStepY; - if(texture2D(landscapeTex[0], centerCoo + fullStepX + fullStepY).r == landscapePx.r) - scalerCoo += 16.0 * scalerStepY; -#else - int iScaler = f2i(landscapePx.a), iRow = iScaler / 8; - scalerCoo.x += float(iScaler - iRow * 8) / 8.0; - scalerCoo.y += float(iScaler / 8) / 32.0; -#endif - - // Note: scalerCoo will jump around a lot, causing some GPUs to - // apparantly get confused with the level-of-detail - // calculation. We therefore try to disable LOD. - vec4 scalerPx = texture2DLod(scalerTex, scalerCoo, 0.0); - - // gen3 other coordinate calculation. Still struggles a bit with 3-ways - vec2 centerCoo2 = centerCoo + fullStep * floor(vec2(-0.5, -0.5) + - scalerPx.gb * 255.0 / 64.0); - vec4 landscapePx2 = texture2D(landscapeTex[0], centerCoo2); - -} - -slice(color+10) { - // Mix second color into main color according to scaler - color = mix(color2, color, scalerPx.r); -} diff --git a/planet/Graphics.ocg/SpriteOverlayShader.glsl b/planet/Graphics.ocg/SpriteOverlayShader.glsl deleted file mode 100644 index 4d10f1cec..000000000 --- a/planet/Graphics.ocg/SpriteOverlayShader.glsl +++ /dev/null @@ -1,11 +0,0 @@ -uniform vec4 overlayClr; -uniform sampler2D overlayTex; - -slice(texture+1) -{ - // Get overlay color from overlay texture - vec4 overlay = baseColor * overlayClr * texture2D(overlayTex, texcoord.xy); - // Mix overlay with texture - float alpha0 = 1.0 - (1.0 - color.a) * (1.0 - overlay.a); - color = vec4(mix(color.rgb, overlay.rgb, overlay.a / alpha0), alpha0); -} diff --git a/planet/Graphics.ocg/SpriteTextureShader.glsl b/planet/Graphics.ocg/SpriteTextureShader.glsl deleted file mode 100644 index 13b916c2b..000000000 --- a/planet/Graphics.ocg/SpriteTextureShader.glsl +++ /dev/null @@ -1,6 +0,0 @@ -uniform sampler2D baseTex; - -slice(texture) -{ - color = baseColor * texture2D(baseTex, texcoord.xy); -} diff --git a/planet/Graphics.ocg/UtilShader.glsl b/planet/Graphics.ocg/UtilShader.glsl deleted file mode 100644 index 307ed54a1..000000000 --- a/planet/Graphics.ocg/UtilShader.glsl +++ /dev/null @@ -1,17 +0,0 @@ - -// #ifdef NO_TEXTURE_LOD_IN_FRAGMENT -#define texture1DLod(t,c,l) texture1D(t,c) -#define texture2DLod(t,c,l) texture2D(t,c) -// #endif - -vec3 extend_normal(vec2 v) -{ - // the higher the second value, the further away the light source from the landscape - return normalize(vec3(v, 0.45)); -} - -// Converts the pixel range 0.0..1.0 into the integer range 0..255 -int f2i(float x) { - return int(x * 255.9); -} - diff --git a/planet/Material.ocg/Gold.ocm b/planet/Material.ocg/Gold.ocm index 42d68a316..8bf108140 100644 --- a/planet/Material.ocg/Gold.ocm +++ b/planet/Material.ocg/Gold.ocm @@ -9,4 +9,7 @@ Blast2ObjectRatio=100 MaxAirSpeed=100 MaxSlide=1 Placement=50 -TextureOverlay=gold \ No newline at end of file +TextureOverlay=gold +LightEmit=0,215,255 +LightSpot=64,64,64 +LightAngle=220 diff --git a/src/graphics/C4DrawGL.cpp b/src/graphics/C4DrawGL.cpp index d3beca553..38ac28d57 100644 --- a/src/graphics/C4DrawGL.cpp +++ b/src/graphics/C4DrawGL.cpp @@ -598,22 +598,15 @@ bool CStdGL::CreateSpriteShader(C4Shader& shader, const char* name, int ssc, C4G shader.AddTexCoord("texcoord"); // Then load slices for fragment shader - shader.AddFragmentSlice(-1, "#define OPENCLONK"); - if (ssc & C4SSC_MOD2) shader.AddFragmentSlice(-1, "#define CLRMOD_MOD2"); + shader.AddFragmentSlice(-1, "#define OPENCLONK\n#define SPRITE"); + if (ssc & C4SSC_MOD2) shader.AddFragmentSlice(-1, "#define HAVE_MOD2"); if (ssc & C4SSC_NORMAL) shader.AddFragmentSlice(-1, "#define HAVE_NORMALMAP"); if (ssc & C4SSC_LIGHT) shader.AddFragmentSlice(-1, "#define HAVE_LIGHT"); - shader.LoadSlices(pGroups, "UtilShader.glsl"); - shader.LoadSlices(pGroups, "ObjectBaseShader.glsl"); + if (ssc & C4SSC_BASE) shader.AddFragmentSlice(-1, "#define HAVE_BASE"); + if (ssc & C4SSC_OVERLAY) shader.AddFragmentSlice(-1, "#define HAVE_OVERLAY"); - if (ssc & C4SSC_BASE) shader.LoadSlices(pGroups, "SpriteTextureShader.glsl"); - if (ssc & C4SSC_OVERLAY) shader.LoadSlices(pGroups, "SpriteOverlayShader.glsl"); - - // In case light is disabled, these shaders use a default light source - // (typically ambient light everywhere). - shader.LoadSlices(pGroups, "ObjectLightShader.glsl"); - shader.LoadSlices(pGroups, "LightShader.glsl"); - shader.LoadSlices(pGroups, "AmbientShader.glsl"); - shader.LoadSlices(pGroups, "GammaShader.glsl"); + shader.LoadSlices(pGroups, "CommonShader.glsl"); + shader.LoadSlices(pGroups, "ObjectShader.glsl"); if (!shader.Init(name, uniformNames)) { diff --git a/src/landscape/C4LandscapeRender.cpp b/src/landscape/C4LandscapeRender.cpp index 2dccdc311..d7a4618b9 100644 --- a/src/landscape/C4LandscapeRender.cpp +++ b/src/landscape/C4LandscapeRender.cpp @@ -537,15 +537,11 @@ bool C4LandscapeRenderGL::LoadShader(C4GroupSet *pGroups, C4Shader& shader, cons if(ssc & C4SSC_LIGHT) hLightTexCoord = shader.AddTexCoord("lightCoord"); // Then load slices for fragment shader - shader.AddFragmentSlice(-1, "#define LANDSCAPE"); + shader.AddFragmentSlice(-1, "#define OPENCLONK\n#define LANDSCAPE"); if(ssc & C4SSC_LIGHT) shader.AddFragmentSlice(-1, "#define HAVE_LIGHT"); // sample light from light texture - shader.LoadSlices(pGroups, "UtilShader.glsl"); + shader.LoadSlices(pGroups, "CommonShader.glsl"); shader.LoadSlices(pGroups, "LandscapeShader.glsl"); - shader.LoadSlices(pGroups, "LightShader.glsl"); - shader.LoadSlices(pGroups, "AmbientShader.glsl"); - shader.LoadSlices(pGroups, "ScalerShader.glsl"); - shader.LoadSlices(pGroups, "GammaShader.glsl"); // Initialise! if (!shader.Init(name, UniformNames)) { @@ -578,7 +574,6 @@ bool C4LandscapeRenderGL::LoadShaders(C4GroupSet *pGroups) UniformNames[C4LRU_Gamma] = "gamma"; UniformNames[C4LRU_Resolution] = "resolution"; UniformNames[C4LRU_Center] = "center"; - UniformNames[C4LRU_MatMap] = "matMap"; UniformNames[C4LRU_MatMapTex] = "matMapTex"; UniformNames[C4LRU_MaterialDepth] = "materialDepth"; UniformNames[C4LRU_MaterialSize] = "materialSize"; @@ -800,7 +795,7 @@ void C4LandscapeRenderGL::AddTexturesFromMap(C4TextureMap *pMap) } -void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap) +void C4LandscapeRenderGL::BuildMatMap(uint32_t *pTex) { // TODO: Still merely an inefficient placeholder for things to come... @@ -812,8 +807,8 @@ void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap) if(!pEntry->GetTextureName()) { // Undefined textures transparent - if(pFMap) pFMap[pix] = 0.5 / iMaterialTextureDepth; - if(pIMap) pIMap[pix] = 0; + pTex[2*pix] = 0; + pTex[2*pix+1] = RGBA(0,0,0,255); continue; } @@ -821,8 +816,13 @@ void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap) int iPhases = 1; const char *p = pEntry->GetTextureName(); while((p = strchr(p, '-'))) { p++; iPhases++; } // Hard-coded hack. Fix me! - const int iPhaseLength = 300; - float phase = (iPhases == 1 ? 0 : float(C4TimeMilliseconds::Now().AsInt() % (iPhases * iPhaseLength)) / iPhaseLength); + C4Material *pMaterial = pEntry->GetMaterial(); + const int iPhaseLength = pMaterial->AnimationSpeed; + float phase = 0; + if (iPhases > 1) { + phase = C4TimeMilliseconds::Now().AsInt() % (iPhases * iPhaseLength); + phase /= iPhaseLength; + } // Find our transition const char *pFrom = pEntry->GetTextureName(); @@ -851,8 +851,17 @@ void C4LandscapeRenderGL::BuildMatMap(GLfloat *pFMap, GLubyte *pIMap) } // Assign texture - if(pFMap) pFMap[pix] = (gTexCoo + 0.5) / iMaterialTextureDepth; - if(pIMap) pIMap[pix] = int((gTexCoo * 256.0 / iMaterialTextureDepth) + 0.5); + int iTexCoo = int((gTexCoo * 256.0 / iMaterialTextureDepth) + 0.5); + pTex[2*pix] = RGBA( + Clamp(pMaterial->LightEmit[0], 0, 255), + Clamp(pMaterial->LightEmit[1], 0, 255), + Clamp(pMaterial->LightEmit[2], 0, 255), + iTexCoo); + pTex[2*pix+1] = RGBA( + Clamp(pMaterial->LightSpot[0], 0, 255), + Clamp(pMaterial->LightSpot[1], 0, 255), + Clamp(pMaterial->LightSpot[2], 0, 255), + Clamp(pMaterial->LightAngle, 0, 255)); } } @@ -889,12 +898,6 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Ligh ShaderCall.SetUniform2f(C4LRU_Center, centerX / float(Surfaces[0]->Wdt), centerY / float(Surfaces[0]->Hgt)); - if (shader->HaveUniform(C4LRU_MatMap)) - { - GLfloat MatMap[256]; - BuildMatMap(MatMap, NULL); - ShaderCall.SetUniform1fv(C4LRU_MatMap, 256, MatMap); - } ShaderCall.SetUniform1i(C4LRU_MaterialDepth, iMaterialTextureDepth); ShaderCall.SetUniform2f(C4LRU_MaterialSize, float(iMaterialWidth) / ::Game.C4S.Landscape.MaterialZoom, @@ -960,9 +963,9 @@ void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo, const C4FoWRegion *Ligh } if(ShaderCall.AllocTexUnit(C4LRU_MatMapTex, GL_TEXTURE_1D)) { - GLubyte MatMap[256]; - BuildMatMap(NULL, MatMap); - glTexImage1D(GL_TEXTURE_1D, 0, 1, 256, 0, GL_RED, GL_UNSIGNED_BYTE, MatMap); + uint32_t MatMap[2*256]; + BuildMatMap(MatMap); + glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA8, 2*256, 0, GL_RGBA, GL_UNSIGNED_BYTE, MatMap); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } diff --git a/src/landscape/C4LandscapeRender.h b/src/landscape/C4LandscapeRender.h index 032858f18..819353519 100644 --- a/src/landscape/C4LandscapeRender.h +++ b/src/landscape/C4LandscapeRender.h @@ -45,7 +45,6 @@ enum C4LR_Uniforms C4LRU_Gamma, C4LRU_Resolution, C4LRU_Center, - C4LRU_MatMap, C4LRU_MatMapTex, C4LRU_MaterialDepth, C4LRU_MaterialSize, @@ -150,7 +149,7 @@ private: void AddTextureTransition(const char *szFrom, const char *szTo); void AddTextureAnim(const char *szTextureAnim); void AddTexturesFromMap(C4TextureMap *pMap); - void BuildMatMap(GLfloat *pFMap, GLubyte *pIMap); + void BuildMatMap(uint32_t *pTex); }; #endif diff --git a/src/landscape/C4Material.cpp b/src/landscape/C4Material.cpp index 4d3198885..cc562f7da 100644 --- a/src/landscape/C4Material.cpp +++ b/src/landscape/C4Material.cpp @@ -267,6 +267,12 @@ void C4MaterialCore::Clear() MinHeightCount = 0; SplashRate=10; KeepSinglePixels=false; + AnimationSpeed = 20; + LightAngle = 255; + for (int i = 0; i < 3; i++) { + LightEmit[i] = 0; + LightSpot[i] = 16; + } } void C4MaterialCore::Default() @@ -304,7 +310,6 @@ void C4MaterialCore::CompileFunc(StdCompiler *pComp) if (pComp->isCompiler()) Clear(); pComp->Name("Material"); pComp->Value(mkNamingAdapt(toC4CStr(Name), "Name", "")); - pComp->Value(mkNamingAdapt(ColorAnimation, "ColorAnimation", 0)); const StdEnumEntry Shapes[] = { @@ -368,6 +373,10 @@ void C4MaterialCore::CompileFunc(StdCompiler *pComp) pComp->Value(mkNamingAdapt(MinHeightCount, "MinHeightCount", 0)); pComp->Value(mkNamingAdapt(SplashRate, "SplashRate", 10)); pComp->Value(mkNamingAdapt(KeepSinglePixels, "KeepSinglePixels", false)); + pComp->Value(mkNamingAdapt(AnimationSpeed, "AnimationSpeed", 100)); + pComp->Value(mkNamingAdapt(LightAngle, "LightAngle", 255)); + pComp->Value(mkNamingAdapt(mkArrayAdaptDM(LightEmit, 0), "LightEmit")); + pComp->Value(mkNamingAdapt(mkArrayAdaptDM(LightSpot, 16),"LightSpot")); pComp->NameEnd(); // material reactions pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(CustomReactionList), diff --git a/src/landscape/C4Material.h b/src/landscape/C4Material.h index 70b674bb0..57d34a5a0 100644 --- a/src/landscape/C4Material.h +++ b/src/landscape/C4Material.h @@ -180,6 +180,10 @@ public: int32_t MinHeightCount; // minimum material thickness in order for it to be counted int32_t SplashRate; bool KeepSinglePixels; // if true, single pixels are not destroyed (for vehicle) + int32_t AnimationSpeed; // frames per animation phase + int32_t LightAngle; // light angle at which we have maximum reflection + int32_t LightEmit[3]; // amount the material lights up itself + int32_t LightSpot[3]; // spot strength void Clear(); void Default(); diff --git a/src/lib/StdMeshMaterial.cpp b/src/lib/StdMeshMaterial.cpp index b6ddbfd79..eb0f8a579 100644 --- a/src/lib/StdMeshMaterial.cpp +++ b/src/lib/StdMeshMaterial.cpp @@ -854,7 +854,6 @@ bool StdMeshMaterialProgram::AddParameterNames(const StdMeshMaterialShaderParame bool StdMeshMaterialProgram::CompileShader(StdMeshMaterialLoader& loader, C4Shader& shader, int ssc) { // Add standard slices - shader.AddFragmentSlice(-1, "#define MESH"); loader.AddShaderSlices(shader, ssc); // Add our slices shader.AddVertexSlice(-1, "varying vec2 texcoord;"); diff --git a/src/object/C4Def.cpp b/src/object/C4Def.cpp index a2ca88720..be9b80e57 100644 --- a/src/object/C4Def.cpp +++ b/src/object/C4Def.cpp @@ -61,27 +61,15 @@ public: { #ifndef USE_CONSOLE // Add mesh-independent slices - shader.AddFragmentSlice(-1, "#define OPENCLONK"); - shader.AddVertexSlice(-1, "#define OPENCLONK"); - - if (ssc & C4SSC_MOD2) shader.AddFragmentSlice(-1, "#define CLRMOD_MOD2"); + shader.AddFragmentSlice(-1, "#define OPENCLONK\n#define MESH"); + shader.AddVertexSlice(-1, "#define OPENCLONK\n#define MESH"); + if (ssc & C4SSC_MOD2) shader.AddFragmentSlice(-1, "#define HAVE_MOD2"); if (ssc & C4SSC_LIGHT) shader.AddFragmentSlice(-1, "#define HAVE_LIGHT"); + if (ssc & C4SSC_BASE) shader.AddFragmentSlice(-1, "#define HAVE_LIGHT"); + if (ssc & C4SSC_OVERLAY) shader.AddFragmentSlice(-1, "#define HAVE_LIGHT"); - shader.LoadSlices(&::GraphicsResource.Files, "UtilShader.glsl"); - shader.LoadSlices(&::GraphicsResource.Files, "ObjectBaseShader.glsl"); - shader.LoadSlices(&::GraphicsResource.Files, "MeshShader.glsl"); - shader.LoadSlices(&::GraphicsResource.Files, "GammaShader.glsl"); - - // Note that these shader slices are always loaded, even if lighting - // is disabled. The shaders then assume a default light if HAVE_LIGHT - // is not defined. This avoids completely flat shading for meshes - // that are shown as picture graphics for example. - shader.LoadSlices(&::GraphicsResource.Files, "ObjectLightShader.glsl"); - shader.LoadSlices(&::GraphicsResource.Files, "LightShader.glsl"); - shader.LoadSlices(&::GraphicsResource.Files, "AmbientShader.glsl"); - - if (ssc & C4SSC_BASE) shader.LoadSlices(&::GraphicsResource.Files, "SpriteTextureShader.glsl"); - if (ssc & C4SSC_OVERLAY) shader.LoadSlices(&::GraphicsResource.Files, "SpriteOverlayShader.glsl"); + shader.LoadSlices(&::GraphicsResource.Files, "CommonShader.glsl"); + shader.LoadSlices(&::GraphicsResource.Files, "ObjectShader.glsl"); #endif }