troisjs / trois

✨ ThreeJS + VueJS 3 + ViteJS ⚡
https://troisjs.github.io
MIT License
4.15k stars 298 forks source link

Adding custom shader #92

Open krumpyzoid opened 2 years ago

krumpyzoid commented 2 years ago

I'm trying to add fresnel shader to my mesh

I did not write the shader myself, I tried turning it into a string as it's what is required. The original uniforms were declared using new THREE.color('black') but I do not have access to the THREE functions it seems

<Mesh ref="box">
            <TorusKnotGeometry :radius="1.5" :tube="0.625" :tubularSegments="300" :radialSegments="200" />
            <LambertMaterial :color="'#0F161B'" />
            <ShaderMaterial :props="{ uniforms: uniforms, vertexShader: vertex, fragmentShader: fragment }" />
</Mesh>
data() {
        return {
        header: 'varying vec3 vNN; varying vec3 vEye;',
        fragmentHeader: 'uniform vec3 fresnelColor;',
        vertex: `
            mat4 LM = modelMatrix;
            LM[2][3] = 0.0;
            LM[3][0] = 0.0;
            LM[3][1] = 0.0;
            LM[3][2] = 0.0;

            vec4 GN = LM * vec4(objectNormal.xyz, 1.0);
            vNN = normalize(GN.xyz);
            vEye = normalize(GN.xyz-cameraPosition);`
        ,
        fragment: `
            gl_FragColor = vec4( outgoingLight, diffuseColor.a );

            gl_FragColor.rgb +=  ( 1.0 - -min(dot(vEye, normalize(vNN) ), 0.0) ) * fresnelColor;
            `
        ,
        uniforms: {
            diffuse: '#ff0000',
            fresnelColor: '#ff0000'
        }
    }
},

Error:

Error 0 - VALIDATE_STATUS false

Program Info Log: Vertex shader is not compiled.

The original setup I copied was using objects so it would return "string.replace is not a function"

Any idea how to do this ? Or a simpler way of adding fresnel ?

Original code

var material = THREE.extendMaterial(THREE.MeshStandardMaterial, {

      // Will be prepended to vertex and fragment code

      header: 'varying vec3 vNN; varying vec3 vEye;',
      fragmentHeader: 'uniform vec3 fresnelColor;',

      // Insert code lines by hinting at a existing

      vertex: {
        // Inserts the line after #include <fog_vertex>
        '#include <fog_vertex>': `

          mat4 LM = modelMatrix;
          LM[2][3] = 0.0;
          LM[3][0] = 0.0;
          LM[3][1] = 0.0;
          LM[3][2] = 0.0;

          vec4 GN = LM * vec4(objectNormal.xyz, 1.0);
          vNN = normalize(GN.xyz);
          vEye = normalize(GN.xyz-cameraPosition);`
      },
      fragment: {
        'gl_FragColor = vec4( outgoingLight, diffuseColor.a );' : `

gl_FragColor.rgb +=  ( 1.0 - -min(dot(vEye, normalize(vNN) ), 0.0) ) * fresnelColor;

`
      },

      // Uniforms (will be applied to existing or added)

      uniforms: {
        diffuse: new THREE.Color( 'black' ),
        fresnelColor: new THREE.Color( 'blue' )
      }

    });

    // mesh
    mesh = new THREE.Mesh( geometry, material );
krumpyzoid commented 2 years ago

I see people accessing THREE.FresnelShader in this post https://discourse.threejs.org/t/material-with-emissionmap-and-fresnel-shader/6223 , but I can't find official three.js doc about it

Is there a way to directly use this somehow ?

klevron commented 2 years ago

You should create your own material component :

import { Color } from 'three'
import { StandardMaterial } from 'troisjs'

export default {
  extends: StandardMaterial,
  created() {
    this.material.onBeforeCompile = (shader) => {
      shader.uniforms.fresnelColor = { value: new Color(0x0000ff) }

      shader.vertexShader = `
        varying vec3 vNN;
        varying vec3 vEye;
      ` + shader.vertexShader

      shader.vertexShader = shader.vertexShader.replace(
        '#include <fog_vertex>',
        `
          #include <fog_vertex>

          mat4 LM = modelMatrix;
          LM[2][3] = 0.0;
          LM[3][0] = 0.0;
          LM[3][1] = 0.0;
          LM[3][2] = 0.0;

          vec4 GN = LM * vec4(objectNormal.xyz, 1.0);
          vNN = normalize(GN.xyz);
          vEye = normalize(GN.xyz-cameraPosition);
        `
      )

      shader.fragmentShader = `
        uniform vec3 fresnelColor;
        varying vec3 vNN;
        varying vec3 vEye;
      ` + shader.fragmentShader

      shader.fragmentShader = shader.fragmentShader.replace(
        '#include <output_fragment>',
        `
          #include <output_fragment>
          gl_FragColor.rgb +=  ( 1.0 - -min(dot(vEye, normalize(vNN) ), 0.0) ) * fresnelColor;
        `
      )
    }
  }
}

image