Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 创建跟随光源的昼/夜着色器_Javascript_Three.js_Shader - Fatal编程技术网

Javascript 创建跟随光源的昼/夜着色器

Javascript 创建跟随光源的昼/夜着色器,javascript,three.js,shader,Javascript,Three.js,Shader,我有一个由定向光照亮的球体来模拟太阳照射地球。我正在尝试添加一个着色器,该着色器将在地球的未照明部分显示夜间的地球,并在白天为照明部分显示地球。我计划最终让方向光围绕地球旋转,更新着色器以显示当前处于阴影中的地球部分。我发现以下代码笔部分满足了我的要求: 在上面的代码笔中,显示的昼夜纹理基于相机相对于地球仪的位置,我需要这些纹理相对于光源的位置而不是相机的位置保持固定 constructor(selector) { this.selector = selector;

我有一个由定向光照亮的球体来模拟太阳照射地球。我正在尝试添加一个着色器,该着色器将在地球的未照明部分显示夜间的地球,并在白天为照明部分显示地球。我计划最终让方向光围绕地球旋转,更新着色器以显示当前处于阴影中的地球部分。我发现以下代码笔部分满足了我的要求:

在上面的代码笔中,显示的昼夜纹理基于相机相对于地球仪的位置,我需要这些纹理相对于光源的位置而不是相机的位置保持固定

    constructor(selector) {
        this.selector = selector;
        this.width = window.innerWidth;
        this.height = window.innerHeight;
        this.frameEvent = new Event('frame');

        this.textureLoader = new THREE.TextureLoader();
    }

    setScene() {
        this.scene = new THREE.Scene();
        this.scenary = new THREE.Object3D;

        this.scene.add(this.scenary);
    }

    setCamera() {
        this.camera = new THREE.PerspectiveCamera(50, this.width/this.height, 1, 20000);
        this.camera.position.y = 25;
        this.camera.position.z = 300;
    }

    setRenderer() {
        this.renderer = new THREE.WebGLRenderer({
            antialias: true
        });
        this.renderer.setSize(this.width, this.height);
        this.canvas = document.querySelector(this.selector).appendChild(this.renderer.domElement);
    }

    setControls() {
        this.controls = new THREE.OrbitControls(this.camera, this.canvas);
        this.controls.maxDistance = 500;
        this.controls.minDistance = 200;
    }

    addHelpers() {
        this.axes = new THREE.AxesHelper(500);
        this.scenary.add(this.axes);
    }

    addLights() {
        this.ambientLight = new THREE.AmbientLight(0x555555);
        this.directionalLight = new THREE.DirectionalLight(0xffffff);
        this.directionalLight.position.set(10, 0, 10).normalize();

        this.scenary.add(this.ambientLight);
        this.scenary.add(this.directionalLight);
    }

    render() {
        this.renderer.render(this.scene, this.camera);
        this.canvas.dispatchEvent(this.frameEvent);
        this.frameRequest = window.requestAnimationFrame(this.render.bind(this));
    }

    destroy() {
        window.cancelAnimationFrame(this.frameRequest);
        this.scene.children = [];
        this.canvas.remove();
    }

    addSky() {
        let radius = 400,
            segments = 50;

        this.skyGeometry = new THREE.SphereGeometry(radius, segments, segments);
        this.skyMaterial = new THREE.MeshPhongMaterial({
            color: 0x666666,
            side: THREE.BackSide,
            shininess: 0
        });
        this.sky = new THREE.Mesh(this.skyGeometry, this.skyMaterial);

        this.scenary.add(this.sky);

        this.loadSkyTextures();
    }

    loadSkyTextures() {
        this.textureLoader.load('https://acaua.gitlab.io/webgl-with-threejs/img/textures/earth/sky-texture.jpg', texture => {
            this.skyMaterial.map = texture;
            this.skyMaterial.needsUpdate = true;
        });
    }

    addEarth() {
        let radius = 100,
            segments = 50;

        this.earthGeometry = new THREE.SphereGeometry(radius, segments, segments);
        this.earthMaterial = new THREE.ShaderMaterial({
            bumpScale: 5,
            specular: new THREE.Color(0x333333),
            shininess: 50,
            uniforms: {
                sunDirection: {
                    value: new THREE.Vector3(1, 1, .5)
                },
                dayTexture: {
                    value: this.textureLoader.load('https://acaua.gitlab.io/webgl-with-threejs/img/textures/earth/earth-texture.jpg')
                },
                nightTexture: {
                    value: this.textureLoader.load('https://acaua.gitlab.io/webgl-with-threejs/img/textures/earth/earth-night.jpg')
                }
            },
            vertexShader: this.dayNightShader.vertex,
            fragmentShader: this.dayNightShader.fragment
        });
        this.earth = new THREE.Mesh(this.earthGeometry, this.earthMaterial);

        this.scenary.add(this.earth);

        this.loadEarthTextures();
        this.addAtmosphere();
    }

    loadEarthTextures() {
        this.textureLoader.load('https://acaua.gitlab.io/webgl-with-threejs/img/textures/earth/earth-texture.jpg', texture => {
            this.earthMaterial.map = texture;
            this.earthMaterial.needsUpdate = true;
        });
        this.textureLoader.load('https://acaua.gitlab.io/webgl-with-threejs/img/textures/earth/earth-bump.jpg', texture => {
            this.earthMaterial.bumpMap = texture;
            this.earthMaterial.needsUpdate = true;
        });
        this.textureLoader.load('https://acaua.gitlab.io/webgl-with-threejs/img/textures/earth/earth-specular.jpg', texture => {
            this.earthMaterial.specularMap = texture;
            this.earthMaterial.needsUpdate = true;
        });
    }

    addAtmosphere() {
        this.innerAtmosphereGeometry = this.earthGeometry.clone();
        this.innerAtmosphereMaterial = THREEx.createAtmosphereMaterial();
        this.innerAtmosphereMaterial.uniforms.glowColor.value.set(0x88ffff);
        this.innerAtmosphereMaterial.uniforms.coeficient.value = 1;
        this.innerAtmosphereMaterial.uniforms.power.value = 5;
        this.innerAtmosphere = new THREE.Mesh(this.innerAtmosphereGeometry, this.innerAtmosphereMaterial);
        this.innerAtmosphere.scale.multiplyScalar(1.008);

        this.outerAtmosphereGeometry = this.earthGeometry.clone();
        this.outerAtmosphereMaterial = THREEx.createAtmosphereMaterial();
        this.outerAtmosphereMaterial.side = THREE.BackSide;
        this.outerAtmosphereMaterial.uniforms.glowColor.value.set(0x0088ff);
        this.outerAtmosphereMaterial.uniforms.coeficient.value = .68;
        this.outerAtmosphereMaterial.uniforms.power.value = 10;
        this.outerAtmosphere = new THREE.Mesh(this.outerAtmosphereGeometry, this.outerAtmosphereMaterial);
        this.outerAtmosphere.scale.multiplyScalar(1.06);

        this.earth.add(this.innerAtmosphere);
        this.earth.add(this.outerAtmosphere);
    }

    get dayNightShader() {
        return {
            vertex: `
                varying vec2 vUv;
                varying vec3 vNormal;

                void main() {
                    vUv = uv;
                    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
                    vNormal = normalMatrix * normal;
                    gl_Position = projectionMatrix * mvPosition;
                }
            `,
            fragment: `
                uniform sampler2D dayTexture;
                uniform sampler2D nightTexture;

                uniform vec3 sunDirection;

                varying vec2 vUv;
                varying vec3 vNormal;

                void main(void) {
                    vec3 dayColor = texture2D(dayTexture, vUv).rgb;
                    vec3 nightColor = texture2D(nightTexture, vUv).rgb;

                    float cosineAngleSunToNormal = dot(normalize(vNormal), sunDirection);

                    cosineAngleSunToNormal = clamp(cosineAngleSunToNormal * 5.0, -1.0, 1.0);

                    float mixAmount = cosineAngleSunToNormal * 0.5 + 0.5;

                    vec3 color = mix(nightColor, dayColor, mixAmount);

                    gl_FragColor = vec4(color, 1.0);
                }
            `
        }
    }

    animate() {
        this.canvas.addEventListener('frame', () => {
            this.scenary.rotation.x += 0.0001;
            this.scenary.rotation.y -= 0.0005;
        });
    }

    init() {
        this.setScene();
        this.setCamera();
        this.setRenderer();
        this.setControls();
        this.addLights();
        this.render();
        this.addSky();
        this.addEarth();
        this.animate();
    }
}

let canvas = new Canvas('#canvas');
canvas.init();
据我所知,该着色器似乎正在由get dayNightShader()中的摄影机更新。看起来modelViewMatrix、projectionMatrix和normalMatrix都是基于相机的,基于我在three.js的文档中可以找到的内容,我尝试将它们更改为固定的矢量位置,但我看到它所做的唯一一件事就是隐藏球体并显示大气纹理。有没有办法使用光源的位置来确定着色器显示的内容,而不是摄影机?

问题在于线条

float cosinanglesuntonormal=dot(归一化(vNormal),sunddirection);
在片段着色器中。
vNormal
是视图空间中的一个方向,因为它由顶点着色器中的
normalMatrix
变换,但
sunddirection
是世界空间方向

要解决此问题,必须通过顶点着色器中的视图矩阵变换日光方向,并将变换后的方向向量传递给片段着色器

vSunDir=mat3(viewMatrix)*sunDirection;
注意,
viewMatrix
从世界空间转换到视图空间。使用
viewMatrix
而不是
normalMatrix
很重要,因为
normalMatrix
从模型空间转换到世界空间

顶点着色器:

可变vec2vuv;
可变vec3 vNormal;
可变vec3-vSunDir;
均匀的vec3太阳方向;
void main(){
vUv=紫外线;
vec4 mvPosition=modelViewMatrix*vec4(位置,1.0);
vNormal=法线矩阵*法线;
vSunDir=mat3(视图矩阵)*太阳方向;
gl_位置=投影矩阵*mvPosition;
}
片段着色器:

均匀纹理;
纹理均匀;
可变vec2 vUv;
可变vec3 vNormal;
可变vec3-vSunDir;
真空总管(真空){
vec3 dayColor=texture2D(dayTexture,vUv).rgb;
vec3 nightColor=texture2D(nightTexture,vUv).rgb;
浮点余弦角SUNTONORMAL=点(规格化(VNOMAL),规格化(vSunDir));
cosinanglesuntonormal=夹具(cosinanglesuntonormal*5.0,-1.0,1.0);
浮动混合量=余弦角SUNTONORMAL*0.5+0.5;
vec3颜色=混合(夜色、日色、混合量);
gl_FragColor=vec4(颜色,1.0);
}
类画布{
构造函数(选择器){
this.selector=选择器;
this.width=window.innerWidth;
this.height=window.innerHeight;
this.frameEvent=新事件(“帧”);
this.textureLoader=新的三个.textureLoader();
}
setScene(){
this.scene=新的三个.scene();
this.scenary=new THREE.Object3D;
this.scene.add(this.sceneary);
}
setCamera(){
this.camera=新的三视角摄像机(50,this.width/this.height,12000);
这个.camera.position.y=25;
这个.camera.position.z=300;
}
setRenderer(){
this.renderer=new THREE.WebGLRenderer({
反别名:对
});
this.renderer.setSize(this.width,this.height);
var container=document.getElementById(this.selector);
this.canvas=container.appendChild(this.renderer.doElement);
//this.canvas=document.querySelector(this.selector).appendChild(this.renderer.domeElement);
}
setControls(){
this.controls=新的三个.OrbitControls(this.camera,this.canvas);
this.controls.maxDistance=500;
this.controls.minDistance=200;
}
addHelpers(){
this.axes=新的三轴辅助(500);
this.scenary.add(this.axes);
}
addLights(){
this.ambientLight=新的三个ambientLight(0x555555);
this.directionalLight=新的三个方向灯(0xffffff);
this.directionalLight.position.set(10,0,10).normalize();
this.scenary.add(this.ambientLight);
this.scenary.add(this.directionalLight);
}
render(){
this.renderer.render(this.scene,this.camera);
this.canvas.dispatchEvent(this.frameEvent);
this.frameRequest=window.requestAnimationFrame(this.render.bind(this));
}
销毁{
window.cancelAnimationFrame(this.frameRequest);
this.scene.children=[];
this.canvas.remove();
}
addSky(){
假设半径=400,
分段=50;
this.skyGeometry=新的三个。球面测量法(半径、分段、分段);
this.skyMaterial=新的3.MeshPhongMaterial({
颜色:0x666666,
侧面:三,背面,
光泽度:0
});
this.sky=new THREE.Mesh(this.skyGeometry,this.skyMaterial);
this.sceneary.add(this.sky);
这是loadSkyTextures();
}
loadSkyTextures(){
此.textureLoader.load('https://acaua.gitlab.io/webgl-with-threejs/img/textures/earth/sky-texture.jpg,纹理=>{
this.skymater.map=纹理;
this.skymater.needsUpdate=true;
});
}
addEarth(){
假设半径=100,
分段=50;
this.earthGeometry=新的三种球墨测量法(半径、分段、分段);
this.earthMaterial=新的三个.ShaderMaterial({
比例:5,
镜面反射:新三色(0x333333),
光泽度:50,,
制服:{
太阳方向:{
值:新的3.Vector3(1,1,5)
},
dayTexture:{
值:this.textureLoader.load('https://acaua.gitlab.io/webgl-with-threejs/img/textures/earth/earth-texture.jpg')
},
夜纹理:{
值:this.textureLoader.load('https://acaua.gitlab.io/webgl-with-threejs/img/textures/earth/earth-night.jpg')
}
},
版本