This is what my texture should look like.
But at three it is completely red.
Other full color exr images are displayed well
This is what my texture should look like.
But at three it is completely red.
Other full color exr images are displayed well
It seems to be a single-channel texture containing only the red channel, marked as texture.format = RedFormat
. THREE.MeshBasicMaterial expects a three-channel RGB texture for its .map
texture slot. Some alternatives:
Thanks for the reply @donmccurdy!
Could you please elaborate on option A?
I used option B, but it makes the texture look more contrasty than it actually is.
const renderToCanvas = (texture) => {
const { width, height } = texture.image;
const renderer = new WebGLRenderer();
renderer.setSize(width, height, false);
// const fsQuadMaterial = new MeshBasicMaterial({
// map: texture,
// });
const fsQuadMaterial = new ShaderMaterial({
uniforms: {
uTexture: { value: texture },
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4(position.xy, 0.0, 1.0);
}
`,
fragmentShader: `
precision highp float;
uniform sampler2D uTexture;
varying vec2 vUv;
void main() {
float r = texture2D(uTexture, vUv).r;
gl_FragColor = vec4(r, r, r, 1.0);
}
`,
});
const fsQuad = new FullScreenQuad(fsQuadMaterial);
fsQuad.render(renderer);
return { canvas: renderer.domElement };
};
Found a solution for incorrect gamma
void main() {
float r = texture2D(uTexture, vUv).r;
r = pow(1.0 / 2.2);
gl_FragColor = vec4(r, r, r, 1.0);
}
Equivalent to the inline gamma correction would be to instead assign the color space of the texture. Not sure which is expected for this texture:
// (A)
texture.colorSpace = THREE.SRGBColorspace;
// (B)
texture.colorSpace = THREE.LinearSRGBColorSpace;
These values ​​don’t seem to work
Ah I see the warning in the console:
THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.
So WebGL can’t do that conversion for you with this texture, your fix in the shader is probably best instead if you’re using a single-channel EXR texture like this.
Thanks for the support and direction! It means a lot to me.
P.S. I found a way to make the shader more “generic”. Maybe it will be useful for others.
https://codesandbox.io/p/sandbox/black-cloud-forked-ykm25r?file=%2Fsrc%2FApp.js%3A72%2C62
import {
WebGLRenderer,
ShaderMaterial,
RGBFormat,
RedFormat,
RGBAFormat,
SRGBColorSpace,
AlphaFormat,
DepthFormat,
DepthStencilFormat,
RGFormat,
LuminanceFormat,
LuminanceAlphaFormat,
RGBAIntegerFormat,
Uniform,
} from "three";
import { EXRLoader, FullScreenQuad } from "three-stdlib";
function getShaderMaterial(texture) {
const format = texture.format;
let colorExpr = "tex";
switch (format) {
case RedFormat:
case AlphaFormat:
case LuminanceFormat:
case DepthFormat:
case DepthStencilFormat:
colorExpr = "vec4(tex.r, tex.r, tex.r, 1.0)";
break;
case RGFormat:
colorExpr = "vec4(tex.r, tex.g, 0.0, 1.0)";
break;
case LuminanceAlphaFormat:
colorExpr = "vec4(tex.r, tex.r, tex.r, tex.g)";
break;
case RGBAIntegerFormat:
colorExpr = "vec4(tex.r, tex.g, tex.b, tex.a) / 255.0";
break;
case RGBFormat:
case RGBAFormat:
colorExpr = "tex";
}
return new ShaderMaterial({
uniforms: {
uTexture: new Uniform(texture),
},
defines: {
SRGB: texture.colorSpace !== SRGBColorSpace,
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = vec4(position.xy, 0.0, 1.0);
}
`,
fragmentShader: `
precision highp float;
uniform sampler2D uTexture;
varying vec2 vUv;
void main() {
vec4 tex = texture2D(uTexture, vUv);
vec4 color = ${colorExpr};
// https://discourse.threejs.org/t/whats-this-about-gammafactor/4264
// https://github.com/mrdoob/three.js/blob/817a222f2d12baf44a38baf256bc9d4f65d82465/examples/jsm/utils/TextureUtils.js#L18
#ifdef SRGB
// https://github.com/mrdoob/three.js/blob/b3cb0cd0d6066f7054a76b90904486e40031c2ce/src/renderers/shaders/ShaderChunk/colorspace_pars_fragment.glsl.js#L11
gl_FragColor = sRGBTransferOETF(color);
#else
gl_FragColor = color;
#endif
}
`,
});
}
const renderToCanvas = (texture) => {
const { width, height } = texture.image;
const renderer = new WebGLRenderer();
renderer.setSize(width, height, false);
const fsQuadMaterial = getShaderMaterial(texture);
const fsQuad = new FullScreenQuad(fsQuadMaterial);
fsQuad.render(renderer);
return { canvas: renderer.domElement };
};