227 lines
8.3 KiB
GLSL
227 lines
8.3 KiB
GLSL
#version 450 core
|
|
|
|
layout (constant_id = 0) const bool FOG = true;
|
|
layout (constant_id = 1) const bool PBR = true;
|
|
layout (constant_id = 2) const bool TRIPLANAR = false;
|
|
layout (constant_id = 3) const bool STOCHASTIC = false;
|
|
layout (constant_id = 4) const bool BLEND = true;
|
|
|
|
layout (constant_id = 16) UNIT_SIZE = 8;
|
|
|
|
// Ouput data
|
|
layout(location = 0) out vec3 color;
|
|
|
|
uniform sampler2DArray TextureAtlas;
|
|
uniform sampler2DArray NormalAtlas;
|
|
uniform sampler2DArray HOSAtlas;
|
|
|
|
uniform mat4 View;
|
|
uniform vec3 FogColor;
|
|
|
|
#ifdef GEOMETRY
|
|
in GeometryData
|
|
#else
|
|
in VertexData
|
|
#endif
|
|
{
|
|
vec3 Position_modelspace;
|
|
#ifdef GEOMETRY
|
|
flat uint Textures[3];
|
|
vec3 TextureRatio;
|
|
#else
|
|
flat uint Texture;
|
|
#endif
|
|
|
|
vec3 FaceNormal_modelspace;
|
|
vec3 FaceNormal_worldspace;
|
|
vec3 EyeDirection_cameraspace;
|
|
vec3 LightDirection_cameraspace;
|
|
|
|
float Depth;
|
|
} vs;
|
|
|
|
vec3 expand(vec3 v) {
|
|
return (v - 0.5) * 2;
|
|
}
|
|
|
|
vec2 hash2D(vec2 s) {
|
|
// magic numbers
|
|
return fract(sin(mod(vec2(dot(s, vec2(127.1, 311.7)), dot(s, vec2(269.5, 183.3))), 3.14159)) * 43758.5453);
|
|
}
|
|
|
|
vec4 textureStochastic(sampler2DArray sample, vec3 UV) {
|
|
if(STOCHASTIC) {
|
|
// triangular by approx 2*sqrt(3)
|
|
vec2 skewUV = mat2(1.0, 0.0, -0.57735027, 1.15470054) * (UV.xy * 3.46400);
|
|
|
|
// vertex id and barrycentric coords
|
|
vec2 vxID = vec2(floor(skewUV));
|
|
vec3 barry = vec3(fract(skewUV), 0);
|
|
barry.z = 1.0 - barry.x - barry.y;
|
|
|
|
vec3 BW_vx0;
|
|
vec3 BW_vx1;
|
|
vec3 BW_vx2;
|
|
vec3 BW_vx3;
|
|
|
|
if(barry.z > 0) {
|
|
BW_vx0 = vec3(vxID, 0);
|
|
BW_vx1 = vec3(vxID + vec2(0, 1), 0);
|
|
BW_vx2 = vec3(vxID + vec2(1, 0), 0);
|
|
BW_vx3 = barry.zyx;
|
|
} else {
|
|
BW_vx0 = vec3(vxID + vec2(1, 1), 0);
|
|
BW_vx1 = vec3(vxID + vec2(1, 0), 0);
|
|
BW_vx2 = vec3(vxID + vec2(0, 1), 0);
|
|
BW_vx3 = vec3(-barry.z, 1.0 - barry.y, 1.0 - barry.x);
|
|
}
|
|
|
|
vec2 dx = dFdx(UV.xy);
|
|
vec2 dy = dFdy(UV.xy);
|
|
|
|
return textureGrad(sample, vec3(UV.xy + hash2D(BW_vx0.xy), UV.z), dx, dy) * BW_vx3.x +
|
|
textureGrad(sample, vec3(UV.xy + hash2D(BW_vx1.xy), UV.z), dx, dy) * BW_vx3.y +
|
|
textureGrad(sample, vec3(UV.xy + hash2D(BW_vx2.xy), UV.z), dx, dy) * BW_vx3.z;
|
|
} else {
|
|
return texture(sample, UV);
|
|
}
|
|
}
|
|
|
|
vec4 getTexture(sampler2DArray sample, vec2 UV) {
|
|
#ifdef GEOMETRY
|
|
if(BLEND) {
|
|
vec4 colx = textureStochastic(sample, vec3(UV, vs.Textures[0]));
|
|
if(vs.Textures[1] == vs.Textures[0]) {
|
|
return vs.Textures[2] == vs.Textures[0] ? colx :
|
|
mix(colx, textureStochastic(sample, vec3(UV, vs.Textures[2])), vs.TextureRatio.z);
|
|
} else {
|
|
vec4 coly = textureStochastic(sample, vec3(UV, vs.Textures[1]));
|
|
return vs.Textures[2] == vs.Textures[0] ? mix(colx, coly, vs.TextureRatio.y) : (
|
|
vs.Textures[2] == vs.Textures[1] ? mix(coly, colx, vs.TextureRatio.x) :
|
|
colx * vs.TextureRatio.x + coly * vs.TextureRatio.y + textureStochastic(sample, vec3(UV, vs.Textures[2])) * vs.TextureRatio.z);
|
|
}
|
|
} else {
|
|
int mainTexture = vs.TextureRatio.x >= vs.TextureRatio.y ?
|
|
(vs.TextureRatio.x >= vs.TextureRatio.z ? 0 : 2) :
|
|
(vs.TextureRatio.y >= vs.TextureRatio.z ? 1 : 2);
|
|
return textureStochastic(sample, vec3(UV, vs.Textures[mainTexture]));
|
|
}
|
|
#else
|
|
return textureStochastic(sample, vec3(UV, vs.Texture));
|
|
#endif
|
|
}
|
|
|
|
vec3 getTriTexture(sampler2DArray sample, vec2 crdx, vec2 crdy, vec2 crdz, vec3 weights) {
|
|
return getTexture(sample, crdx).rgb * weights.x +
|
|
getTexture(sample, crdy).rgb * weights.y +
|
|
getTexture(sample, crdz).rgb * weights.z;
|
|
}
|
|
|
|
void main() {
|
|
float texScale = 1. / UNIT_SIZE;
|
|
if(TRIPLANAR) {
|
|
// Triplanar
|
|
float plateauSize = 0.001;
|
|
float transitionSpeed = 2;
|
|
|
|
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
|
|
blendWeights = blendWeights - plateauSize;
|
|
blendWeights = pow(max(blendWeights, 0), vec3(transitionSpeed));
|
|
vec2 UVx = vs.Position_modelspace.yz * texScale;
|
|
vec2 UVy = vs.Position_modelspace.zx * texScale;
|
|
vec2 UVz = vs.Position_modelspace.xy * texScale;
|
|
|
|
vec3 tex = getTriTexture(TextureAtlas, UVx, UVy, UVz, blendWeights);
|
|
|
|
if(PBR) {
|
|
// Whiteout normal blend
|
|
vec3 texNx = expand(getTexture(NormalAtlas, UVx).rgb);
|
|
vec3 texNy = expand(getTexture(NormalAtlas, UVy).rgb);
|
|
vec3 texNz = expand(getTexture(NormalAtlas, UVz).rgb);
|
|
// Swizzle world normals into tangent space and apply Whiteout blend
|
|
texNx = vec3(texNx.xy + vs.FaceNormal_worldspace.zy, abs(texNx.z) * vs.FaceNormal_worldspace.x);
|
|
texNy = vec3(texNy.xy + vs.FaceNormal_worldspace.xz, abs(texNy.z) * vs.FaceNormal_worldspace.y);
|
|
texNz = vec3(texNz.xy + vs.FaceNormal_worldspace.xy, abs(texNz.z) * vs.FaceNormal_worldspace.z);
|
|
// Swizzle tangent normals to match world orientation and triblend
|
|
vec3 worldNormal = normalize(texNx.zyx * blendWeights.x + texNy.xzy * blendWeights.y +texNz.xyz * blendWeights.z);
|
|
|
|
vec3 texHOS = getTriTexture(HOSAtlas, UVx, UVy, UVz, blendWeights);
|
|
}
|
|
} else {
|
|
// Cheap planar
|
|
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
|
|
vec3 nrm = normalize(pow(blendWeights, vec3(80 / sqrt(UNIT_SIZE))));
|
|
vec2 UV = (vec2(vs.Position_modelspace.xy * nrm.z) + vec2(vs.Position_modelspace.yz * nrm.x) + vec2(vs.Position_modelspace.zx * nrm.y)) * texScale;
|
|
|
|
vec3 tex = getTexture(TextureAtlas, UV).rgb;
|
|
if(PBR) {
|
|
vec3 texN = expand(getTexture(NormalAtlas, UV).rgb);
|
|
// Swizzle world normals into tangent space and apply Whiteout blend
|
|
// Swizzle tangent normals to match world orientation and triblend
|
|
vec3 worldNormal = normalize(vec3(texN.xy + vs.FaceNormal_worldspace.zy, abs(texN.z) * vs.FaceNormal_worldspace.x).zyx * blendWeights.x +
|
|
vec3(texN.xy + vs.FaceNormal_worldspace.xz, abs(texN.z) * vs.FaceNormal_worldspace.y).xzy * blendWeights.y +
|
|
vec3(texN.xy + vs.FaceNormal_worldspace.xy, abs(texN.z) * vs.FaceNormal_worldspace.z).xyz * blendWeights.z);
|
|
|
|
vec3 texHOS = getTexture(HOSAtlas, UV).rgb;
|
|
}
|
|
}
|
|
|
|
// Colors
|
|
if(PBR) {
|
|
// Texture properties
|
|
vec3 TextureDiffuseColor = tex;
|
|
vec3 TextureAmbientColor = vec3(.1) * TextureDiffuseColor * texHOS.y;
|
|
vec3 TextureSpecularColor = vec3(.8) * texHOS.z;
|
|
|
|
vec3 Normal_cameraspace = normalize((View * vec4(worldNormal,0)).xyz);
|
|
|
|
// Light emission properties
|
|
// You probably want to put them as uniforms
|
|
vec3 LightColor = vec3(1, 0.9, 0.9);
|
|
float LightPower = 1.2f;
|
|
|
|
// Distance to the light
|
|
float distance = 1.0f;//length( LightPosition_worldspace - Position_worldspace );
|
|
|
|
// Direction of the light (from the fragment to the light)
|
|
vec3 l = normalize(vs.LightDirection_cameraspace);
|
|
// Cosine of the angle between the normal and the light direction,
|
|
// clamped above 0
|
|
// - light is at the vertical of the triangle -> 1
|
|
// - light is perpendiular to the triangle -> 0
|
|
// - light is behind the triangle -> 0
|
|
float cosTheta = clamp(dot(Normal_cameraspace,l), 0,1 );
|
|
|
|
// Eye vector (towards the camera)
|
|
vec3 E = normalize(vs.EyeDirection_cameraspace);
|
|
// Direction in which the triangle reflects the light
|
|
vec3 R = reflect(-l,Normal_cameraspace);
|
|
// Cosine of the angle between the Eye vector and the Reflect vector,
|
|
// clamped to 0
|
|
// - Looking into the reflection -> 1
|
|
// - Looking elsewhere -> < 1
|
|
float cosAlpha = clamp( dot( E,R ), 0,1 );
|
|
|
|
float visibility=1.0;
|
|
|
|
// MAYBE: shadow
|
|
|
|
color =
|
|
// Ambient : simulates indirect lighting
|
|
TextureAmbientColor +
|
|
// Diffuse : "color" of the object
|
|
visibility * TextureDiffuseColor * LightColor * LightPower * cosTheta / (distance * distance) +
|
|
// Specular : reflective highlight, like a mirror
|
|
visibility * TextureSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance * distance);
|
|
} else {
|
|
color = tex;
|
|
}
|
|
if(FOG) {
|
|
float ratio = exp(vs.Depth * 0.69)-1;
|
|
color = mix(color, pow(FogColor, vec3(2.2)), clamp(ratio, 0, 1));
|
|
}
|
|
color = pow(color, vec3(1.0 / 2.2));
|
|
if(color.r > 1 || color.g > 1 || color.b > 1) {
|
|
color = vec3(1, 0, 0); //TODO: bloom
|
|
}
|
|
} |