diff --git a/src/webgl/material.js b/src/webgl/material.js index 3fa865b64a..894785c8c6 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -2590,6 +2590,11 @@ function material(p5, fn) { * `loadModel()` apply their own normal map from * the `.mtl` file's `map_Bump`. * + * Note: On a shape whose texture coordinates wrap all the way around, such as + * `sphere()`, the two edges of the image meet. The + * image has to tile for them to line up, otherwise a seam shows where they + * join. + * * Note: `normalTexture()` can only be used in WebGL mode. * * @method normalTexture @@ -2682,6 +2687,13 @@ function material(p5, fn) { * * A light source is needed to see the effect. * + * Note: On a shape whose texture coordinates wrap all the way around, such as + * `sphere()`, the two edges of the image meet. The + * image has to tile for them to line up. Because a bump map is read by + * comparing neighbouring pixels, also call + * `textureWrap(REPEAT)` so those comparisons + * carry across the join instead of stopping at the edge. + * * Note: `bumpTexture()` can only be used in WebGL mode. * * @method bumpTexture diff --git a/src/webgl/shaders/phong.frag b/src/webgl/shaders/phong.frag index 527826f460..64e39d0b2c 100644 --- a/src/webgl/shaders/phong.frag +++ b/src/webgl/shaders/phong.frag @@ -76,12 +76,17 @@ void main(void) { vec3 mapN; if (uNormalMapMode == 1) { // bump map: brightness is height, so the tangent-space normal comes from - // how fast that height changes between neighbouring texels. - float h = TEXTURE(uNormalSampler, vTexCoord).r; - float hu = TEXTURE(uNormalSampler, vTexCoord + vec2(uNormalTexelSize.x, 0.0)).r; - float hv = TEXTURE(uNormalSampler, vTexCoord + vec2(0.0, uNormalTexelSize.y)).r; + // how fast that height changes between neighbouring texels. sampling both + // sides keeps the slope right at the edges of the map, where reaching past + // one side would otherwise clamp and read back the same texel. + vec2 du = vec2(uNormalTexelSize.x, 0.0); + vec2 dv = vec2(0.0, uNormalTexelSize.y); + float hl = TEXTURE(uNormalSampler, vTexCoord - du).r; + float hr = TEXTURE(uNormalSampler, vTexCoord + du).r; + float hd = TEXTURE(uNormalSampler, vTexCoord - dv).r; + float hu = TEXTURE(uNormalSampler, vTexCoord + dv).r; // the surface leans away from the direction height increases in - mapN = normalize(vec3(h - hu, h - hv, 1.0)); + mapN = normalize(vec3((hl - hr) * 0.5, (hd - hu) * 0.5, 1.0)); } else { // normal map: rgb already holds the tangent-space normal mapN = TEXTURE(uNormalSampler, vTexCoord).rgb * 2.0 - 1.0; diff --git a/src/webgpu/shaders/material.js b/src/webgpu/shaders/material.js index 8541967642..5288c3b9cf 100644 --- a/src/webgpu/shaders/material.js +++ b/src/webgpu/shaders/material.js @@ -401,12 +401,17 @@ ${useTextureMaps ? ` if (material.uHasNormalMap == 1) { var mapN: vec3; if (material.uNormalMapMode == 1u) { // bump map: brightness is height, so the tangent-space normal comes from - // how fast that height changes between neighbouring texels. - let h = textureSample(uNormalSampler, uNormalSampler_sampler, input.vTexCoord).r; - let hu = textureSample(uNormalSampler, uNormalSampler_sampler, input.vTexCoord + vec2(material.uNormalTexelSize.x, 0.0)).r; - let hv = textureSample(uNormalSampler, uNormalSampler_sampler, input.vTexCoord + vec2(0.0, material.uNormalTexelSize.y)).r; + // how fast that height changes between neighbouring texels. sampling both + // sides keeps the slope right at the edges of the map, where reaching past + // one side would otherwise clamp and read back the same texel. + let du = vec2(material.uNormalTexelSize.x, 0.0); + let dv = vec2(0.0, material.uNormalTexelSize.y); + let hl = textureSample(uNormalSampler, uNormalSampler_sampler, input.vTexCoord - du).r; + let hr = textureSample(uNormalSampler, uNormalSampler_sampler, input.vTexCoord + du).r; + let hd = textureSample(uNormalSampler, uNormalSampler_sampler, input.vTexCoord - dv).r; + let hu = textureSample(uNormalSampler, uNormalSampler_sampler, input.vTexCoord + dv).r; // the surface leans away from the direction height increases in - mapN = normalize(vec3(h - hu, h - hv, 1.0)); + mapN = normalize(vec3((hl - hr) * 0.5, (hd - hu) * 0.5, 1.0)); } else { // normal map: rgb already holds the tangent-space normal mapN = textureSample(uNormalSampler, uNormalSampler_sampler, input.vTexCoord).rgb * 2.0 - 1.0;