Проблемы с отражением и глянцем Threejs с ShaderMaterial - PullRequest
3 голосов
/ 26 сентября 2019

Я пытаюсь разработать свой собственный шейдер GLSL для ThreeJS, но я использую глянцевые отражения с изображениями HDR.Он отлично работает с изображениями LDR, но не с HDR.

Я начал с использования в этом примере для генерации mipmaps.

Затем я использовал расширение браузерачтобы получить скомпилированный код из MeshStandardMaterial по умолчанию, я выделил нужные мне биты, которые были связаны с картами окружения, и я написал свой шейдер (я добавил в шейдер функции сияния и освещенности), но я, вероятно, чего-то не хватаетпоскольку мне кажется, что это не работает.

На изображении ниже вы можете видеть, что в версии 1) с простым HDRI (без мип-карт) мне пришлось изменить тип данных на «ТРИ».FloatType».Хотя это все еще выглядит неправильно (не глянцево и очень темно), это самое близкое, что мне удалось получить.Версия 2) использует по умолчанию «THREE.UnsignedDataType», но изображение выглядит совершенно неправильно.Версия 3), которая включает в себя мипмапы, только ошибки.

enter image description here

Вот ссылка , чтобы загрузить все файлы илипросто взгляните на код ниже.

<html>
    <head>
        <script src="./js/three.min.js"></script>
        <script src="./js/RGBELoader.js"></script>
        <script src="./js/HDRCubeTextureLoader.js"></script>
        <script src="./js/PMREMCubeUVPacker.js"></script>
        <script src="./js/PMREMGenerator.js"></script>
        <style>
            html, body{ margin:0px; padding: 0px;}
        </style>
    </head>

    <body>
        <!-- Vertex and Fragment shaders-->
        <script id="VS" type="x-shader/x-vertex">
            varying vec3 vNormal;
            varying vec3 vViewPosition;

            void main() {
                vNormal = normal;
                vViewPosition = - (modelViewMatrix * vec4( position, 1.0 )).xyz;

                gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
            }
        </script>
        <script id="FS" type="x-shader/x-fragment">
            varying vec3 vViewPosition;
            varying vec3 vNormal;

            uniform int maxMipLevel;
            uniform samplerCube envMap;
            uniform float envMapIntensity;
            uniform float flipEnvMap;

            uniform float roughness;


            float pow2( const in float x ) {
                return x*x;
            }
            float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {
                return ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );
            }
            float getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {
                float maxMIPLevelScalar = float( maxMIPLevel );
                float desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );
                return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );
            }
            vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {
                return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );
            }
            vec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float blinnShininessExponent, const in int maxMIPLevel ) {
                vec3 reflectVec = reflect( -viewDir, normal );
                reflectVec = inverseTransformDirection( reflectVec, viewMatrix );
                float specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );

                vec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );
                vec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );
                envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;

                return envMapColor.rgb * envMapIntensity * .75;
            }
            vec3 getLightProbeIndirectIrradiance( const in vec3 normal, const in int maxMIPLevel ) {
                vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );
                vec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );
                vec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );

                return PI * envMapColor.rgb * envMapIntensity;
            }


            void main() {
                vec3 irradiance = getLightProbeIndirectIrradiance(normalize(vNormal), maxMipLevel );
                vec3 radiance = getLightProbeIndirectRadiance( normalize( vViewPosition ), normalize(vNormal), GGXRoughnessToBlinnExponent( roughness ), maxMipLevel );

                gl_FragColor = vec4( radiance, 1.0 );
            }

        </script>

        <!-- THREE JS code-->
        <script>
            var CubeTextureLoader = new THREE.CubeTextureLoader();
            var HDRCubeTextureLoader = new THREE.HDRCubeTextureLoader();

            // renderer
            var renderer = new THREE.WebGLRenderer({antialias:true});
            renderer.setClearColor( 0xaaaaaa );
            renderer.setSize( window.innerWidth, window.innerHeight );
            document.body.appendChild( renderer.domElement );

            // scene
            scene = new THREE.Scene();

            // camera
            camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
            camera.position.set(0, 0, 40);

            // load HDRIs and Generate mipmaps
            // Code from: https://threejs.org/examples/#webgl_materials_envmaps_hdr
            var hdrCubeRenderTarget;
            var hdrUrls = [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ];
            var hdrCubeMap = new THREE.HDRCubeTextureLoader()
            .setPath( './pisaHDR/' )
            .setDataType( THREE.UnsignedByteType )
            // .setDataType( THREE.FloatType )
            .load( hdrUrls, function () {
                var pmremGenerator = new THREE.PMREMGenerator( hdrCubeMap );
                pmremGenerator.update( renderer );
                var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
                pmremCubeUVPacker.update( renderer );
                hdrCubeRenderTarget = pmremCubeUVPacker.CubeUVRenderTarget;
                hdrCubeMap.magFilter = THREE.LinearFilter;
                hdrCubeMap.needsUpdate = true;
                pmremGenerator.dispose();
                pmremCubeUVPacker.dispose();
            } );

            // materials
            var stdMtl = new THREE.MeshStandardMaterial( { color: 0xffffff, roughness: 0.5, metalness: 1.0 } );
            var cusMtl = new THREE.ShaderMaterial( {
                defines: {
                    PI: 3.14159265359
                },
                uniforms: {
                    roughness: 0.5,
                    envMapIntensity: { value:1.0 },
                    flipEnvMap: { value: -1.0 },
                    envMap: { value:null }
                },
                vertexShader: document.getElementById('VS').text,
                fragmentShader: document.getElementById('FS').text
            } );

            // geometries
            var sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 );
            var stdSphereMesh = new THREE.Mesh( sphereGeometry, stdMtl );
            var cusSphereMesh = new THREE.Mesh( sphereGeometry, cusMtl );
            stdSphereMesh.position.set(-7.5, 0, 0);
            cusSphereMesh.position.set(7.5, 0, 0);

            scene.add( stdSphereMesh );
            scene.add( cusSphereMesh );

            // render scene
            function render() {
                var newEnvMap = hdrCubeRenderTarget ? hdrCubeRenderTarget.texture : null;
                if ( newEnvMap && newEnvMap !== stdSphereMesh.material.envMap ) {
                    stdSphereMesh.material.envMap = newEnvMap;
                    stdSphereMesh.material.needsUpdate = true;

                    cusSphereMesh.material.uniforms.envMap.value = newEnvMap; // This isErroring
                    // cusSphereMesh.material.uniforms.envMap.value = hdrCubeMap; // Result show HDRI but with wrong gamma and no mipmaps
                    cusSphereMesh.material.needsUpdate = true;
                }

                requestAnimationFrame( render );
                renderer.render( scene, camera );
            }
            render();

        </script>

    </body>
</html>
...