Commit e8080bff authored by Shu's avatar Shu 🍠
Browse files

Biplanar mapping

parent c213acc1
......@@ -98,8 +98,7 @@ Released as `0.0.1`: `Pre alpha 1`
- [ ] Merge stochastic and triplanar
- https://assetstore.unity.com/packages/tools/terrain/microsplat-96478
- https://www.youtube.com/user/slipster216/videos
- [ ] Biplanar
- Stochastic
- [x] Biplanar
- [ ] Distance resampling
- [ ] Tesselation
- [ ] Planet scale LOD (using chunk level average)
......
......@@ -45,7 +45,7 @@ vec2 hash2D(vec2 s) {
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) {
vec4 textureStochasticGrad(sampler2DArray sample, vec3 UV, vec2 dx, vec2 dy) {
#ifdef STOCHASTIC
// triangular by approx 2*sqrt(3)
vec2 skewUV = mat2(1.0, 0.0, -0.57735027, 1.15470054) * (UV.xy * 3.46400);
......@@ -72,12 +72,16 @@ vec4 textureStochastic(sampler2DArray sample, vec3 UV) {
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 textureGrad(sample, UV, dx, dy);
#endif
}
vec4 textureStochastic(sampler2DArray sample, vec3 UV) {
#ifdef STOCHASTIC
return textureStochasticGrad(sample, UV, dFdx(UV.xy), dFdy(UV.xy));
#else
return texture(sample, UV);
#endif
......@@ -106,23 +110,54 @@ vec4 getTexture(sampler2DArray sample, vec2 UV) {
return textureStochastic(sample, vec3(UV, vs.Texture));
#endif
}
vec4 getTextureGrad(sampler2DArray sample, vec2 UV, vec2 dx, vec2 dy) {
#ifdef GEOMETRY
#ifdef BLEND
vec4 colx = textureStochasticGrad(sample, vec3(UV, vs.Textures[0]), dx, dy);
if(vs.Textures[1] == vs.Textures[0]) {
return vs.Textures[2] == vs.Textures[0] ? colx :
mix(colx, textureStochasticGrad(sample, vec3(UV, vs.Textures[2]), dx, dy), vs.TextureRatio.z);
} else {
vec4 coly = textureStochasticGrad(sample, vec3(UV, vs.Textures[1]), dx, dy);
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 + textureStochasticGrad(sample, vec3(UV, vs.Textures[2]), dx, dy) * 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 textureStochasticGrad(sample, vec3(UV, vs.Textures[mainTexture]), dx, dy);
#endif
#else
return textureStochasticGrad(sample, vec3(UV, vs.Texture), dx, dy);
#endif
}
vec4 getTriTexture(sampler2DArray sample, vec2 UVx, vec2 UVy, vec2 UVz, vec3 w) {
vec4 x = getTexture(sample, UVx);
vec4 y = getTexture(sample, UVy);
vec4 z = getTexture(sample, UVz);
return x*w.x + y*w.y + z*w.z;
}
vec4 getTriTexture(sampler2DArray sample, vec2 crdx, vec2 crdy, vec2 crdz, vec3 weights) {
return getTexture(sample, crdx) * weights.x +
getTexture(sample, crdy) * weights.y +
getTexture(sample, crdz) * weights.z;
vec4 getBiTexture(sampler2DArray sample, vec2 UVa, vec2 UVb, vec2 dxa, vec2 dxb, vec2 dya, vec2 dyb, vec2 w) {
vec4 x = getTextureGrad(sample, UVa, dxa, dya);
vec4 y = getTextureGrad(sample, UVb, dxb, dyb);
return x*w.x + y*w.y;
}
void main() {
float texScale = 1. / UNIT_SIZE;
float transitionSpeed = 2;
#ifdef TRIPLANAR
// Triplanar
float plateauSize = 0.001;
float transitionSpeed = 2;
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
blendWeights = blendWeights - plateauSize;
blendWeights = normalize(pow(max(blendWeights, 0), vec3(transitionSpeed)));
blendWeights = pow(max(blendWeights, 0), vec3(transitionSpeed));
blendWeights = blendWeights / (blendWeights.x + blendWeights.y + blendWeights.z);
vec2 UVx = vs.Position_modelspace.yz * texScale;
vec2 UVy = vs.Position_modelspace.zx * texScale;
vec2 UVz = vs.Position_modelspace.xy * texScale;
......@@ -143,6 +178,54 @@ void main() {
vec3 texHOS = getTriTexture(HOSAtlas, UVx, UVy, UVz, blendWeights).rgb;
#endif
#else
#ifdef BIPLANAR
// Biplanar
vec3 pos = vs.Position_modelspace * texScale;
vec3 dpdx = dFdx(pos);
vec3 dpdy = dFdy(pos);
vec3 blendWeights = abs(normalize(vs.FaceNormal_modelspace));
// determine major axis (in x; yz are following axis)
ivec3 ma = (blendWeights.x>blendWeights.y && blendWeights.x>blendWeights.z) ? ivec3(0,1,2) :
(blendWeights.y>blendWeights.z) ? ivec3(1,2,0) : ivec3(2,0,1);
// determine minor axis (in x; yz are following axis)
ivec3 mi = (blendWeights.x<blendWeights.y && blendWeights.x<blendWeights.z) ? ivec3(0,1,2) :
(blendWeights.y<blendWeights.z) ? ivec3(1,2,0) : ivec3(2,0,1);
// determine median axis (in x; yz are following axis)
ivec3 me = ivec3(3) - mi - ma;
vec2 UVa = vec2(pos[ma.y], pos[ma.z]);
vec2 UVb = vec2(pos[me.y], pos[me.z]);
vec2 dxa = vec2(dpdx[ma.y], dpdx[ma.z]);
vec2 dxb = vec2(dpdx[me.y], dpdx[me.z]);
vec2 dya = vec2(dpdy[ma.y], dpdy[ma.z]);
vec2 dyb = vec2(dpdy[me.y], dpdy[me.z]);
// blend factors
vec2 w = vec2(blendWeights[ma.x],blendWeights[me.x]);
w = clamp((w-0.5773)/(1.0-0.5773), 0.0, 1.0);
w = pow(w, vec2(transitionSpeed/8));
w = w / (w.x + w.y);
vec4 tex = getBiTexture(TextureAtlas, UVa, UVb, dxa, dxb, dya, dyb, w);
#ifdef PBR
// Whiteout normal blend
vec3 texNa = expand(getTextureGrad(NormalAtlas, UVa, dxa, dya).rgb);
vec3 texNb = expand(getTextureGrad(NormalAtlas, UVb, dxb, dyb).rgb);
// Swizzle world normals into tangent space and apply Whiteout blend
texNa = normalize(vec3(texNa.xy + vs.FaceNormal_worldspace.zy, abs(texNa.z) * vs.FaceNormal_worldspace.x).zyx * blendWeights.x +
vec3(texNa.xy + vs.FaceNormal_worldspace.xz, abs(texNa.z) * vs.FaceNormal_worldspace.y).xzy * blendWeights.y +
vec3(texNa.xy + vs.FaceNormal_worldspace.xy, abs(texNa.z) * vs.FaceNormal_worldspace.z).xyz * blendWeights.z);
texNb = normalize(vec3(texNb.xy + vs.FaceNormal_worldspace.zy, abs(texNb.z) * vs.FaceNormal_worldspace.x).zyx * blendWeights.x +
vec3(texNb.xy + vs.FaceNormal_worldspace.xz, abs(texNb.z) * vs.FaceNormal_worldspace.y).xzy * blendWeights.y +
vec3(texNb.xy + vs.FaceNormal_worldspace.xy, abs(texNb.z) * vs.FaceNormal_worldspace.z).xyz * blendWeights.z);
// Swizzle tangent normals to match world orientation and biblend
vec3 worldNormal = normalize((texNa * w.x + texNb * w.y) / (w.x + w.y));
vec3 texHOS = getBiTexture(HOSAtlas, UVa, UVb, dxa, dxb, dya, dyb, w).rgb;
#endif
#else
// Cheap planar
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
......@@ -152,8 +235,7 @@ void main() {
vec4 tex = getTexture(TextureAtlas, UV);
#ifdef 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
// Swizzle tangent normals to match world orientation
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);
......@@ -161,6 +243,7 @@ void main() {
vec3 texHOS = getTexture(HOSAtlas, UV).rgb;
#endif
#endif
#endif
// Colors
#ifdef PBR
......@@ -173,8 +256,8 @@ void main() {
// Light emission properties
// You probably want to put them as uniforms
vec3 LightColor = vec3(1, 0.9, 0.9);
float LightPower = 1.2f;
vec3 LightColor = vec3(1, 0.9, 0.75);
float LightPower = 1.25f;
// Distance to the light
float distance = 1.0f;//length( LightPosition_worldspace - Position_worldspace );
......@@ -214,7 +297,7 @@ void main() {
#else
color = tex;
#endif
#if FOG
#ifdef FOG
float ratio = exp(vs.Depth * 0.69)-1;
color = mix(color, vec4(pow(FogColor, vec3(2.2)), 1), clamp(ratio, 0, 1));
#endif
......
......@@ -3,9 +3,10 @@
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 = 2) const bool BIPLANAR = false;
layout (constant_id = 3) const bool TRIPLANAR = false;
layout (constant_id = 4) const bool STOCHASTIC = false;
layout (constant_id = 5) const bool BLEND = true;
// ...
layout (constant_id = 16) const int UNIT_SIZE = 8;
......@@ -54,7 +55,7 @@ vec2 hash2D(vec2 s) {
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 smpl, vec3 UV) {
vec4 textureStochasticGrad(sampler2DArray smpl, vec3 UV, vec2 dx, vec2 dy) {
if(STOCHASTIC) {
// triangular by approx 2*sqrt(3)
vec2 skewUV = mat2(1.0, 0.0, -0.57735027, 1.15470054) * (UV.xy * 3.46400);
......@@ -81,12 +82,16 @@ if(STOCHASTIC) {
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(smpl, vec3(UV.xy + hash2D(BW_vx0.xy), UV.z), dx, dy) * BW_vx3.x +
textureGrad(smpl, vec3(UV.xy + hash2D(BW_vx1.xy), UV.z), dx, dy) * BW_vx3.y +
textureGrad(smpl, vec3(UV.xy + hash2D(BW_vx2.xy), UV.z), dx, dy) * BW_vx3.z;
} else {
return textureGrad(smpl, UV, dx, dy);
}
}
vec4 textureStochastic(sampler2DArray smpl, vec3 UV) {
if(STOCHASTIC) {
return textureStochasticGrad(smpl, UV, dFdx(UV.xy), dFdy(UV.xy));
} else {
return texture(smpl, UV);
}
......@@ -115,26 +120,57 @@ if(BLEND) {
return textureStochastic(smpl, vec3(UV, vs.Texture));
#endif
}
vec4 getTextureGrad(sampler2DArray smpl, vec2 UV, vec2 dx, vec2 dy) {
#ifdef GEOMETRY
if(BLEND) {
vec4 colx = textureStochasticGrad(smpl, vec3(UV, vs.Textures[0]), dx, dy);
if(vs.Textures[1] == vs.Textures[0]) {
return vs.Textures[2] == vs.Textures[0] ? colx :
mix(colx, textureStochasticGrad(smpl, vec3(UV, vs.Textures[2]), dx, dy), vs.TextureRatio.z);
} else {
vec4 coly = textureStochasticGrad(smpl, vec3(UV, vs.Textures[1]), dx, dy);
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 + textureStochasticGrad(smpl, vec3(UV, vs.Textures[2]), dx, dy) * 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 textureStochasticGrad(smpl, vec3(UV, vs.Textures[mainTexture]), dx, dy);
}
#else
return textureStochasticGrad(smpl, vec3(UV, vs.Texture), dx, dy);
#endif
}
vec4 getTriTexture(sampler2DArray smpl, vec2 UVx, vec2 UVy, vec2 UVz, vec3 w) {
vec4 x = getTexture(smpl, UVx);
vec4 y = getTexture(smpl, UVy);
vec4 z = getTexture(smpl, UVz);
return x*w.x + y*w.y + z*w.z;
}
vec4 getTriTexture(sampler2DArray smpl, vec2 crdx, vec2 crdy, vec2 crdz, vec3 weights) {
return getTexture(smpl, crdx) * weights.x +
getTexture(smpl, crdy) * weights.y +
getTexture(smpl, crdz) * weights.z;
vec4 getBiTexture(sampler2DArray smpl, vec2 UVa, vec2 UVb, vec2 dxa, vec2 dxb, vec2 dya, vec2 dyb, vec2 w) {
vec4 x = getTextureGrad(smpl, UVa, dxa, dya);
vec4 y = getTextureGrad(smpl, UVb, dxb, dyb);
return x*w.x + y*w.y;
}
void main() {
float texScale = 1. / UNIT_SIZE;
float transitionSpeed = 2;
vec4 tex;
vec3 worldNormal, texHOS;
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));
blendWeights = blendWeights / (blendWeights.x + blendWeights.y + blendWeights.z);
vec2 UVx = vs.Position_modelspace.yz * texScale;
vec2 UVy = vs.Position_modelspace.zx * texScale;
vec2 UVz = vs.Position_modelspace.xy * texScale;
......@@ -155,6 +191,53 @@ if(PBR) {
texHOS = getTriTexture(HOSAtlas, UVx, UVy, UVz, blendWeights).rgb;
}
} else if(BIPLANAR) {
// Biplanar
vec3 pos = vs.Position_modelspace * texScale;
vec3 dpdx = dFdx(pos);
vec3 dpdy = dFdy(pos);
vec3 blendWeights = abs(normalize(vs.FaceNormal_modelspace));
// determine major axis (in x; yz are following axis)
ivec3 ma = (blendWeights.x>blendWeights.y && blendWeights.x>blendWeights.z) ? ivec3(0,1,2) :
(blendWeights.y>blendWeights.z) ? ivec3(1,2,0) : ivec3(2,0,1);
// determine minor axis (in x; yz are following axis)
ivec3 mi = (blendWeights.x<blendWeights.y && blendWeights.x<blendWeights.z) ? ivec3(0,1,2) :
(blendWeights.y<blendWeights.z) ? ivec3(1,2,0) : ivec3(2,0,1);
// determine median axis (in x; yz are following axis)
ivec3 me = ivec3(3) - mi - ma;
vec2 UVa = vec2(pos[ma.y], pos[ma.z]);
vec2 UVb = vec2(pos[me.y], pos[me.z]);
vec2 dxa = vec2(dpdx[ma.y], dpdx[ma.z]);
vec2 dxb = vec2(dpdx[me.y], dpdx[me.z]);
vec2 dya = vec2(dpdy[ma.y], dpdy[ma.z]);
vec2 dyb = vec2(dpdy[me.y], dpdy[me.z]);
// blend factors
vec2 w = vec2(blendWeights[ma.x],blendWeights[me.x]);
w = clamp((w-0.5773)/(1.0-0.5773), 0.0, 1.0);
w = pow(w, vec2(transitionSpeed/8.0));
w = w / (w.x + w.y);
tex = getBiTexture(TextureAtlas, UVa, UVb, dxa, dxb, dya, dyb, w);
if(PBR) {
// Whiteout normal blend
vec3 texNa = expand(getTextureGrad(NormalAtlas, UVa, dxa, dya).rgb);
vec3 texNb = expand(getTextureGrad(NormalAtlas, UVb, dxb, dyb).rgb);
// Swizzle world normals into tangent space and apply Whiteout blend
texNa = normalize(vec3(texNa.xy + vs.FaceNormal_worldspace.zy, abs(texNa.z) * vs.FaceNormal_worldspace.x).zyx * blendWeights.x +
vec3(texNa.xy + vs.FaceNormal_worldspace.xz, abs(texNa.z) * vs.FaceNormal_worldspace.y).xzy * blendWeights.y +
vec3(texNa.xy + vs.FaceNormal_worldspace.xy, abs(texNa.z) * vs.FaceNormal_worldspace.z).xyz * blendWeights.z);
texNb = normalize(vec3(texNb.xy + vs.FaceNormal_worldspace.zy, abs(texNb.z) * vs.FaceNormal_worldspace.x).zyx * blendWeights.x +
vec3(texNb.xy + vs.FaceNormal_worldspace.xz, abs(texNb.z) * vs.FaceNormal_worldspace.y).xzy * blendWeights.y +
vec3(texNb.xy + vs.FaceNormal_worldspace.xy, abs(texNb.z) * vs.FaceNormal_worldspace.z).xyz * blendWeights.z);
// Swizzle tangent normals to match world orientation and biblend
worldNormal = normalize((texNa * w.x + texNb * w.y) / (w.x + w.y));
texHOS = getBiTexture(HOSAtlas, UVa, UVb, dxa, dxb, dya, dyb, w).rgb;
}
} else {
// Cheap planar
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
......
......@@ -4,8 +4,8 @@
layout (constant_id = 0) const bool FOG = true;
layout (constant_id = 1) const bool PBR = true;
// FS spe
layout (constant_id = 5) const bool DO_CURVATURE = false;
layout (constant_id = 6) const bool CURV_DEPTH = true;
layout (constant_id = 8) const bool DO_CURVATURE = false;
layout (constant_id = 9) const bool CURV_DEPTH = true;
layout (binding = 0) uniform UniformBufferObject {
mat4 view;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment