1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
|
#version 420 compatibility
#extension GL_ARB_uniform_buffer_object : enable
#ifdef USE_SSBO
#extension GL_ARB_shader_storage_buffer_object : require
#endif
//#extension GL_ARB_shading_language_420pack: enable
//#define MAX_DECALS_PER_GROUP 48
//#define MAX_DECALS_GROUPS 300
//#define MAX_DECALS 300
//#define USE_PARALLAX
//#define USE_SSBO
//#define DEBUG
struct SDecal {
vec3 pos;
float alpha;
vec2 invSize;
vec2 rotMatrixElements;
vec4 texCoords;
vec4 texNormalsCoords;
};
struct SDecalGroup {
vec4 boundAABB[2];
int ids[MAX_DECALS_PER_GROUP];
};
//FIXME layout(std140)
layout(packed) uniform decalGroupsUBO
{
SDecalGroup groups[MAX_DECALS_GROUPS];
};
#ifdef USE_SSBO
layout(std140) readonly buffer decalsUBO
#else
layout(std140) uniform decalsUBO
#endif
{
SDecal decals[MAX_DECALS];
};
SDecal GetDecalInfo(int id) {
return decals[id];
}
layout(binding=0) uniform sampler2D decalAtlasTex;
layout(binding=1) uniform sampler2D groundNormalsTex;
layout(binding=3) uniform sampler2D infoTex;
layout(binding=4) uniform sampler2D depthTex;
//FIXME make UBO?
uniform vec3 camPos;
uniform vec2 invScreenSize;
uniform mat4 viewProjMatrixInv;
uniform vec2 invMapSize;
uniform vec2 invMapSizePO2;
layout(std140) uniform SGroundLighting
{
uniform vec3 ambientColor;
uniform vec3 diffuseColor;
uniform vec3 specularColor;
uniform vec3 dir;
uniform vec3 fogColor;
uniform float fogEnd;
uniform float fogScale;
} groundLighting;
#ifdef HAVE_SHADOWS
uniform mat4 shadowMatrix;
uniform float shadowDensity;
layout(binding=2) uniform sampler2DShadow shadowTex;
#endif
flat in int decalGroupId;
vec3 ReconstructWorldPos() {
vec2 screenCoord = (gl_FragCoord.st - vec2(0.5)) * invScreenSize;
float z = texture2D(depthTex, screenCoord).x;
vec4 worldPos4 = viewProjMatrixInv * vec4(screenCoord, z, 1.0);
return worldPos4.xyz / worldPos4.w;
}
vec3 GroundNormal(vec3 worldPos, vec3 normal) {
vec3 groundNormal;
groundNormal.xz = texture2D(groundNormalsTex, worldPos.xz * invMapSize).ra;
groundNormal.y = sqrt(1.0 - dot(groundNormal.xz, groundNormal.xz));
//normal = groundNormal;
vec3 front = normalize(cross(groundNormal, vec3(1.,0.,0.)));
vec3 right = cross(groundNormal, front);
normal = mat3x3(right, groundNormal, front) * normal;
return normal;
}
vec3 GetDiffuseLighting(vec3 worldPos, vec3 normal) {
float normalSunCos = max(0.0, dot(normal, groundLighting.dir));
vec3 diffuse = groundLighting.diffuseColor * normalSunCos;
#ifdef HAVE_INFOTEX
diffuse += texture2D(infoTex, worldPos.xz * invMapSizePO2).rgb;
diffuse -= vec3(0.5);
#endif
return diffuse;
}
float GetShadowOcclusion(vec3 worldPos) {
float shadowInt = 1.0;
#ifdef HAVE_SHADOWS
vec4 vertexShadowPos = shadowMatrix * vec4(worldPos, 1.0);
shadowInt = shadow2DProj(shadowTex, vertexShadowPos).r;
shadowInt = mix(1.0, shadowInt, shadowDensity);
#endif
return shadowInt;
}
float ParallaxMapping(inout vec2 ntx, const SDecal d, const mat2 rotMatrix, const vec3 eyeDir)
{
#ifdef USE_PARALLAX
const float layerDepth = 1.0 / 5.0;
vec2 texViewDir = (rotMatrix * eyeDir.xz) * 0.06 * layerDepth;
vec2 txn = mix(d.texNormalsCoords.st, d.texNormalsCoords.pq, ntx);
float height = 1.0 - texture2D(decalAtlasTex, txn).a;
float curHeight = 0.0;
float prevHeight = 0.0;
//FIXME for-loop
// steep mapping
while (curHeight < height) {
ntx -= texViewDir;
txn = mix(d.texNormalsCoords.st, d.texNormalsCoords.pq, ntx); //FIXME
prevHeight = height;
height = 1.0 - texture2D(decalAtlasTex, txn).a;
curHeight += layerDepth;
}
// Parallax Occlusion Mapping
float nextH = height - curHeight;
float prevH = prevHeight - curHeight + layerDepth;
float weight = nextH / (nextH - prevH);
ntx = mix(ntx, ntx + texViewDir, weight);
return 1.0;
// soft self-shadow mapping
/*height = mix(height, prevHeight + layerDepth, weight);
float shadowMultiplier = 0.0;
// calculate initial parameters
float invNumLayers = 1.0 / 4.0;
float layerHeight = height * invNumLayers;
vec2 texStep = (rotMatrix * groundLighting.dir.xz) * 0.06 * invNumLayers;
// current parameters
curHeight = height - layerHeight;
float stepIndex = 1.0;
vec2 stx = ntx;
// while point is below depth 0.0 )
while (curHeight > 0.0) {
stx += texStep;
vec2 stxn = mix(d.texNormalsCoords.st, d.texNormalsCoords.pq, stx); //FIXME
float diffHeight = curHeight - texture(decalAtlasTex, stxn).r;
// if point is under the surface
float underSurface = step(diffHeight, 0.0);
// calculate partial shadowing factor
float newShadowMultiplier = diffHeight * (1.0 - stepIndex * invNumLayers);
shadowMultiplier = max(shadowMultiplier, newShadowMultiplier * underSurface);
// offset to the next layer
stepIndex += 1.0; //FIXME optimize
curHeight -= layerHeight;
}
// calculate lighting only for surface oriented to the light source
//if(dot(vec3(0, 0, 1), L) > 0)
return (1.0 - shadowMultiplier);*/
#else
return 1.0;
#endif
}
#ifdef DEBUG
const vec4 colors[8] = {
vec4(1.0, 0.0, 0.0, 0.75),
vec4(0.0, 1.0, 0.0, 0.75),
vec4(0.0, 0.0, 1.0, 0.75),
vec4(1.0, 1.0, 0.0, 0.75),
vec4(1.0, 0.0, 1.0, 0.75),
vec4(0.0, 1.0, 1.0, 0.75),
vec4(1.0, 1.0, 1.0, 0.75),
vec4(0.0, 0.0, 0.0, 0.75)
};
#endif
void main() {
gl_FragColor = vec4(0.0);
SDecalGroup g = groups[decalGroupId];
// PROJECTION
vec3 worldPos = ReconstructWorldPos();
vec3 eyeDir = (camPos - worldPos);
float eyeDist = length(eyeDir);
eyeDir /= eyeDist;
/*bool outside1 = any(greaterThan(worldPos.xz, g.boundAABB[1].xy)); //FIXME
bool outside2 = any(lessThan(worldPos.xz, g.boundAABB[0].xy));
if (any(bvec2(outside1, outside2))) {
discard;
}*/
vec4 albedo = vec4(vec3(0.0), 1.0);
vec3 normal = vec3(0., 1., 0.);
for (int i = 0; i<MAX_DECALS_PER_GROUP; ++i) {
SDecal d = GetDecalInfo(g.ids[i]);
mat2 rotMatrix = mat2(d.rotMatrixElements.x, -d.rotMatrixElements.y, d.rotMatrixElements.y, d.rotMatrixElements.x);
// WORLD SPACE -> DECAL OBJECT SPACE
vec3 relPos = worldPos - d.pos;
vec2 ntx = (rotMatrix * relPos.xz) * d.invSize.xy; // range: -1 .. 0 .. +1
ntx = ntx * 0.5 + 0.5; // range: 0..+1
//
float shadowMultiplier = ParallaxMapping(ntx, d, rotMatrix, eyeDir);
bool outside = any(greaterThan(abs(ntx - vec2(0.5)), vec2(0.5)));
// improves cache hit-rate for non-rendered texels
ntx = clamp(ntx, vec2(0.0), vec2(1.0));
//ntx = mix(ntx, vec2(0.5), outside);
// TEXTURING
vec2 tx = mix(d.texCoords.st, d.texCoords.pq, ntx);
vec4 albedoD = texture2D(decalAtlasTex, tx);
albedoD.rgb *= shadowMultiplier; //FIXME
#ifdef DEBUG
albedoD = colors[i];
#endif
albedoD.a *= clamp(1.0 - abs(relPos.y) * d.invSize.x, 0.0, 1.0); // make transparent when terrain is higher or lower than the decal's pos
//albedoC.rgb = mix(albedoC.rgb, vec3(cos(float(i) *1.5), sin(float(i) *1.5), 0.0), 0.5);
albedoD.a = mix(albedoD.a * d.alpha, 0.0, outside);
albedo.rgb = mix(albedo.rgb, albedoD.rgb, albedoD.a);
albedo.a *= (1.0 - albedoD.a);
// NORMAL MAPPING
vec2 txn = mix(d.texNormalsCoords.st, d.texNormalsCoords.pq, ntx);
vec3 normalD = texture2D(decalAtlasTex, txn).rbg;
normalD = (normalD * 2.0) - 1.0;
//normalD.y = sqrt(1.0 - dot(normalD.xz, normalD.xz));
normalD.xz = rotMatrix * normalD.xz;
normal = mix(normal, normalD, albedoD.a);
}
//if (albedo.a == 0.0) {
// discard;
//}
// transform normal to GroundNormal space
normal = normalize(normal);
normal = GroundNormal(worldPos, normal);
// LIGHTING (albedo & shadow)
vec3 diffuseTerm = GetDiffuseLighting(worldPos, normal);
float shadowInt = GetShadowOcclusion(worldPos);
// calculate Specular Term:
//FIXME add glossyMap
vec3 R = normalize(-reflect(groundLighting.dir, normal));
vec3 specTerm = groundLighting.specularColor * 2.0 * clamp( pow(max(dot(R, eyeDir),0.0), 5.13), 0.0, 1.0);
// COMBINE
vec3 lightCol = (specTerm + diffuseTerm) * shadowInt + groundLighting.ambientColor;
gl_FragColor.rgb = albedo.rgb * lightCol;
gl_FragColor.a = albedo.a;
// FOG
float fogFactor = (groundLighting.fogEnd - eyeDist) * groundLighting.fogScale;
fogFactor = clamp(fogFactor, 0.0, 1.0);
gl_FragColor.rgb = mix(groundLighting.fogColor * (1.0 - albedo.a), gl_FragColor.rgb, fogFactor); //FIXME
#ifdef DEBUG
gl_FragColor = mix(gl_FragColor, vec4(1.0,0.0,0.0,0.0), 0.2);
#endif
}
|