最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

three.js - Alpha Gradient over mesh geometry in shader code - Stack Overflow

programmeradmin1浏览0评论

I have this scene and for the half transparent box geometry I want to create a shader that gradually reduced the alpha value of gl_FragColor going over the long axis (in this case the z axis in three js).

I am a bit at a loss on how to do that. I thought I could do that with UVs but they run along the wrong axis.

I basically need a way to find out at which percentage point I am on the z axis relative to the mesh size.

Is that even possible in a shader?

I could also send the length size via a uniform. But then again, I missing the overall concept on how to reach the goal. Any help would be greatly appreciated.

I have this scene and for the half transparent box geometry I want to create a shader that gradually reduced the alpha value of gl_FragColor going over the long axis (in this case the z axis in three js).

I am a bit at a loss on how to do that. I thought I could do that with UVs but they run along the wrong axis.

I basically need a way to find out at which percentage point I am on the z axis relative to the mesh size.

Is that even possible in a shader?

I could also send the length size via a uniform. But then again, I missing the overall concept on how to reach the goal. Any help would be greatly appreciated.

Share Improve this question asked Mar 27 at 20:56 Johannes KlaußJohannes Klauß 11.1k18 gold badges71 silver badges127 bronze badges 1
  • The z-axis mentioned is in object space, isn't it? If so, then you could add your another float attribute for each vertex, which is 0 on one end of the z-axis and 1 on the other end. Set this float as output for the VS and input for the FS, where these will arrive interpolated (from 0 to 1) along the z-axis and could then be used as the alpha value. Depending on how the geometry (i.e. positions) of the box are defined, you could also use the position's z-values in the VS directly to set the output float. Does this answer your question? – Yun Commented Mar 28 at 4:11
Add a comment  | 

2 Answers 2

Reset to default 2

Yes it is possible to be done with shaders, here is function which will return material which you can set for your mesh to achieve effect you want:

function materialGradientByAxis(mesh, color = 0xff_ff_00, axis = 0, reverse = false) {
    /*- mesh: must be instanceof THREE.Mesh
            mesh must have geometry (mesh.geometry)
            expected static geometry attribute, but dynamic transform matrix
      - axis: 0: x , 1: y, 2: z (in mesh.geometry space)
      - reverse:
            false: axis local min: alpha = 0, max: alpha = 1
            true: axis local min: alpha = 1, max: alpha = 0
    */

    if ( !(mesh?.geometry?.boundingBox instanceof THREE.Box3) ) {
        mesh.geometryputeBoundingBox()
    }

    const shaderMaterial = new THREE.ShaderMaterial({
        uniforms: {
            u_bboxMin: { value: mesh.geometry.boundingBox.min },
            u_bboxDelta: { value: mesh.geometry.boundingBox.max.clone().sub(mesh.geometry.boundingBox.min) },
            u_axisIndex: { value: axis },
            u_reverse: { value: reverse },
            u_color: { value: new THREE.Color(color) }
        },
        vertexShader:`
            varying vec3 vPosition;

            void main() {
                vPosition = position;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }`,
        fragmentShader:`
            varying vec3 vPosition;
            
            uniform vec3 u_bboxMin;
            uniform vec3 u_bboxDelta;
            uniform int u_axisIndex;
            uniform bool u_reverse;
            uniform vec3 u_color;

            void main() {
                float pos = ((vPosition - u_bboxMin)/u_bboxDelta)[u_axisIndex];
                
                vec4 color = vec4(
                    u_color,
                    u_reverse ? 1.0 - pos : pos
                );
    
                gl_FragColor = color;
            }`
    });
    
    shaderMaterial.transparent = true;
    shaderMaterial.side = THREE.DoubleSide;
    shaderMaterial.depthWrite = false;
    
    return shaderMaterial;
}

you can use it like this:

const geometry = new THREE.BoxGeometry( 2, 2, 2 );
const cube = new THREE.Mesh( geometry );

cube.material = materialGradientByAxis( cube, 0xff_ff_00, 0, false )

and here is a result: and without box helper: you can check full code and view result from different angles here: https://codepen.io/andromeda2/full/LEYJMEN

You can control each pixel via transparency of box or any geometry, by smoothstep function you can calculate transparency based on x-coords of the fragment. Then alpha value determines transparency of fragment.

  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.z = 3;
  const renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
  const geometry = new THREE.BoxGeometry(2, 1, 1);
  const settings = {
    fade: 1.0,
    fadePoint: 0.0
  };
  const material = new THREE.ShaderMaterial({
    uniforms: {
      uFade: {
        value: settings.fade
      },
      uFadePoint: {
        value: settings.fadePoint
      },
      uFadeRange: {
        value: 1.0
      }
    },
    vertexShader: `
    varying vec3 vPosition;
    void main() {
      vPosition = position;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
    fragmentShader: `
    uniform float uFade;
    uniform float uFadePoint;
    uniform float uFadeRange;
    varying vec3 vPosition;
    void main() {
      float alpha = smoothstep(uFadePoint, uFadePoint + uFadeRange, vPosition.x) * uFade;
      gl_FragColor = vec4(0.0, 0.5, 1.0, alpha);
    }
  `,
    transparent: true,
    depthWrite: false
  });
  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);
  const gui = new GUI();
  gui.add(settings, 'fade', 0, 1).name('Fade Strength').onChange(value => {
    material.uniforms.uFade.value = value;
  });
  gui.add(settings, 'fadePoint', -3, 3).name('Fade Point (World X)').onChange(value => {
    material.uniforms.uFadePoint.value = value;
  });

  function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
  }
  animate();
  window.addEventListener('resize', () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  });

https://codepen.io/Lucas_Mas/full/yyLxWEM

发布评论

评论列表(0)

  1. 暂无评论