Search code examples
three.jsshaderwebgltextures

Split up R, G and B channels of texture – apply linear filtering to a subset of them in Three.js/WebGL/Shaders


I am working with a height texture that is retrieved from a server. It encodes the overall height value per pixel within the RGB channels. To get a height value per pixel, the R channel is multiplied by 65536.0, G with 256.0 , then height = R + G + B.

RGB height texture

Due to this splitting, R and G have discontinuities (see the image of the Gchannel below with the 100% white vs. 100% black areas) which they need since the other part (R or B) takes over. That's why I can't linearly filter the texture. I'd like, however, to linearly filter the B channel as it contains the highest frequencies/smallest height deviations – in order to get a smooth surface.

enter image description here

Is it possible to split up the texture internally (e.g., within the shader but I don't mind where this happens), so that only its B channel is linearly filtered while the R, B channels are left at nearest neighbour? Before all components are added together again? If so, how can this be done in an efficient manner?


Solution

  • WebGL texture filtering applies to the whole texture, not each individual channel. But what you could do is clone the texture, and sample them separately in the shader.

    var texture2 = texture1.clone();
    texture1.magFilter = THREE.NearestFilter;
    texture2.magFilter = THREE.LinearFilter; // This is the default
    
    

    Then in your shader:

    vec2 sampleRG = texture2D(texture1, vUv).rg;
    float sampleB = texture2D(texture2, vUv).b;
    

    It's a little bit of extra overhead, but nothing your GPU can't handle.

    Note, I've always seen each channel's value range from 0 to 255, not 256. Are you sure you don't want to multiply by 255 instead?