被照亮的对象的纹理在aframe

时间:2018-04-26 22:54:46

标签: shader aframe

有没有办法在aframe中为照明对象部件设置特殊纹理?例如,我想为地球设置夜间纹理,其中一侧未被照亮的另一面具有另一种纹理。

编辑:我似乎需要某种着色器,但我找不到任何有用的问题。

编辑2:着色器必须灵活,必须使用不同类型的光源和不是完美球体的物体。

3 个答案:

答案 0 :(得分:3)

下面的演示代码。三个文件index.htmlsky.jsearth.js

  1. 主要从loading texturesfragment shaders的两个样本拼接在一起。主要贡献是:

    1-1。着色器worldDayTex: { type: 'map', is: 'uniform' }中的行uniform sampler2D worldDayTex;earth.js。这里是WebGL着色器 声明输入的纹理贴图均匀。

    1-2。行skyEl.setAttribute('material', 'worldDayTex', '#worldDay' ); index.html。这里,纹理贴图从HTML-> WebGL输入并分配给制服。

  2. 白天遮蔽地球,你可能想知道太阳落在地球上一个特定的点上。我们能安全地认为太阳是你唯一的光源吗?如果是这样的话,它就是每个给定点的地球表面法线的简单方程,与太阳的方向。因此,这个例子不是基于采样光强度本身,而是基于两个矢量之间的等式。

  3. 动画看起来很慢(在我的测试中为5 fps),我不知道为什么。官方片段着色器示例的方式相同。

  4. 您需要提供自己的纹理worldDay.pngworldNight.png。为了测试,我使用了this site上的图片。

  5. 可能很明显,但在使用之前,您需要修补aframe-master.min.js的路径,或将其下载到同一目录进行测试。

  6. 在Windows上使用64位版本的Firefox版本进行测试。在我的测试中,Chrome无法支持A-Frame纹理处理。

  7. 的index.html:

    <!DOCTYPE html>
    <html>
    <head>
    
    <meta charset="utf-8">
    <title>Earth day to night example — A-Frame</title>
    <meta name="description" content="Earth day to night example — A-Frame">
    <script src="./aframe-master.min.js"></script>
    <script src="./sky.js"></script>
    <script src="./earth.js"></script>
    </head>
    <body>
    
    <script>
        AFRAME.registerComponent('sun-position-setter', {
            init: function () {
                var skyEl = this.el;
                var orbitEl = this.el.sceneEl.querySelector('#orbit');
    
                orbitEl.addEventListener('componentchanged',
                    function changeSun (evt)
                    {
                        var sunPosition;
                        var phase;
                        var phi;
    
                        if (evt.detail.name !== 'rotation') { return; }
    
                        sunPosition = orbitEl.getAttribute('rotation');
    
                        if(sunPosition === null) { return; }
    
                        phase = (sunPosition.y / 360); // varies from 0 to 1
                        phi = 2 * Math.PI * (phase - 0.5); // varies from 0 to two pi
                        skyEl.setAttribute('material', 'sunDirection',
                            {
                                x: Math.cos(phi),   // use x and y to indicate 2D rotation vector
                                y: Math.sin(phi),
                                z: phase            // use z to indicate the phase
                            }
                        );
                        skyEl.setAttribute('material', 'worldDayTex', '#worldDay' );
                        skyEl.setAttribute('material', 'worldNightTex', '#worldNight' );
                    }
                );
            }
        });
    </script>
    
    <a-scene background="color: #ECECEC">
    <a-assets>
    <img id="worldDay" src="./worldDay.png">
    <img id="worldNight" src="./worldNight.png">
    </a-assets>
    
    <a-entity id="earth" position="0 0 -5" geometry="primitive: sphere; radius: 2" material="shader: earth" sun-position-setter>
        <a-entity id="orbit">
            <a-animation attribute="rotation" from="0 0 0" to="0 360 0" dur="5000" repeat="indefinite" easing="linear"></a-animation>
        </a-entity>
    </a-entity>
    
    <a-entity id="sky" geometry="primitive: sphere; radius: 100;" material="shader: sky; side: back" sun-position-setter>
    </a-entity>
    
    </a-scene>
    </body>
    </html>
    

    sky.js

    /* global AFRAME */
    AFRAME.registerShader('sky', {
      schema: {
        worldDayTex: { type: 'map', is: 'uniform' },
        worldNightTex: { type: 'map', is: 'uniform' },
        sunDirection: { type: 'vec3', default: 'vec3(1,0,0)', is: 'uniform' }
      },
    
      vertexShader:
        `  // use backtick for multi-line text
        varying vec3 vWorldPosition;
        varying vec2 vUV;
    
        void main() {
            vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
            vWorldPosition = worldPosition.xyz;
            vUV = uv;
    
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }
        `
      ,
    
      fragmentShader: 
        `  // use backtick for multi-line text
        uniform vec3 sunDirection;
        varying vec3 vWorldPosition;
        varying vec2 vUV;
    
        void main() 
        {
            vec2 sunUV = vec2( sunDirection.z, 0.5 );
            vec2 cmpUV = vec2( 1.0-vUV.x, vUV.y );
            vec2 diffUV = vec2( sunUV.x-cmpUV.x, sunUV.y-cmpUV.y );
    
            float dist = sqrt( (diffUV.x*diffUV.x) + (diffUV.y*diffUV.y) );
    
            if( dist<0.01 )
                gl_FragColor.rgb = vec3(1,0.98,0.7);
            else
                gl_FragColor.rgb = vec3(0,0,0);
    
            gl_FragColor.a = 1.0;
        }
        `
    
    });
    

    earth.js

    /* global AFRAME */
    AFRAME.registerShader('earth', {
      schema: {
        worldDayTex: { type: 'map', is: 'uniform' },
        worldNightTex: { type: 'map', is: 'uniform' },
        sunDirection: { type: 'vec3', default: 'vec3(1,0,0)', is: 'uniform' }
      },
    
      vertexShader:
        `  // use backtick for multi-line text
        varying vec3 vWorldPosition;
        varying vec2 vUV;
        varying vec3 vNormal;
    
        void main() {
            vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
            vWorldPosition = worldPosition.xyz;
            vUV = uv;
            vNormal = normal;
    
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }
        `
      ,
    
      fragmentShader:
        `  // use backtick for multi-line text
        uniform sampler2D worldDayTex;
        uniform sampler2D worldNightTex;
        uniform vec3 sunDirection;
    
        varying vec3 vWorldPosition;
        varying vec2 vUV;
        varying vec3 vNormal;
    
        void main()
        {
            vec3 worldDayColor = texture2D(worldDayTex, vUV).rgb;
            vec3 worldNightColor = texture2D(worldNightTex, vUV).rgb;
    
            // 2D rotational direction of the sun is stored in x and y
            // but we need it to rotate around the y axis, so shuffle components
            vec3 sunDir = vec3( sunDirection.x, 0, sunDirection.y );
    
            // sunFactor +1 means sun directly overhead, noon
            // sunFactor -1 means sun directly behind, midnight',
            // sunFactor 0 means sun at horizon, sunset or sunrise',
            float sunFactor = dot( vNormal, sunDir );
    
            // amplify so we tend more towards pure day or pure night
            if( sunFactor>0.0 )
                sunFactor = sqrt( sunFactor );
            else
                sunFactor = -sqrt( -sunFactor );
    
            float sunFactorNorm = (sunFactor + 1.0) * 0.5;
    
            gl_FragColor.rgb = mix( worldNightColor, worldDayColor, sunFactorNorm );
    
            gl_FragColor.a = 1.0;
        }
        `
    });
    

答案 1 :(得分:2)

非常粗略(因为如果你是3D的新手,这是很多工作):

  1. 创建2个地球纹理。 1整天和1整夜。
  2. 创建一个片段着色器组件,用于接收2个纹理和太阳光位置。
  3. 使用光线和地球像素位置之间的点积来确定像素是否在阳光下。
  4. 如果在阳光下,采样日纹理像素并将其放入渲染目标;如果没有,请对夜间纹理像素进行采样并将其放入渲染目标;

答案 2 :(得分:1)

 I Hope this will solve your issue


<!DOCTYPE html>
<html>
  <head>
    <title>Hello, WebVR! - A-Frame</title>
    <meta name="description" content="Hello, WebVR! - A-Frame">
    <script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script>
  </head>
  <body>
    <a-scene>
      <a-box position="-1 0.5 -3" rotation="0 45 0" width="1" height="3" color="#4CC3D9" shadow></a-box>     
      <a-plane position="0 0 -3" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow></a-plane>
      <a-sky color="#ECECEC"></a-sky>
    </a-scene>
  </body>
</html>