【问题标题】:Texture for illuminated objects in aframe框架中照明对象的纹理
【发布时间】:2018-05-17 13:41:26
【问题描述】:

有没有办法为框架中的照明对象部分设置特殊纹理?例如,我想为地球设置一个夜间纹理,其中没有被照亮的一侧有另一个纹理。

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

编辑 2:着色器必须灵活,并且必须与不同类型的光源和不是完美球体的对象一起工作。

【问题讨论】:

  • 您是否特别希望通过入射光量来确定出现的纹理?还是只是简单地根据对象的每个部分面向的方向确定要使用哪种纹理?
  • 第一。我想根据光量改变纹理。光有方向是不够的。
  • 有趣的问题

标签: shader aframe


【解决方案1】:

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

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

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

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

  2. 从白天到黑夜对地球进行着色,您可能想知道有多少阳光照射在地球上的给定点上。我们可以安全地假设太阳是你唯一的光源吗?如果是这样,它是每个给定点的地球表面法线与太阳方向的简单方程。因此,这个例子不是基于采样光强度本身,而只是两个向量之间的方程。

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

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

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

  6. 在 Windows 上的 64 位 Firefox 版本 59 中测试。在我的测试中,Chrome 不支持 A-Frame 纹理处理。

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;
    }
    `
});

【讨论】:

  • 不错的方法,但我需要一个更灵活的解决方案,因为我有多个光源并且需要将它与聚光灯一起使用。
  • 聚光灯的计算类似但更复杂。取光在空间中的位置,减去阴影点的表面位置,归一化那个差向量,这就是从光到阴影点的单位向量。该向量与光所指向的单位向量方向之间的角度表示阴影点是否在光的光斑内。此外,着色器通常对允许的灯光数量有一个固定限制,例如 8 个。他们计算每一个并将照明结果相加。
  • 另外,如果您想要聚光灯着色器中的照明方程,或者如果您对着色器中的照明有其他问题,请发布一个单独的问题,我可以在那里提供更完整的答案。
【解决方案2】:

粗略地说(因为如果您是 3D 新手,工作量很大):

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

【讨论】:

    【解决方案3】:
     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>
    

    【讨论】:

    • 你能指定什么部分吗?除了投射阴影之外,这些对象并没有做太多的事情,但只要没有光源,它们就不会做任何事情。但我不想投下阴影。我想根据物体的光照来改变纹理。
    • @Niko Lang 你的意思是手性效果纹理然后尝试添加手性图像
    • 我找不到任何对这个词有用的东西。你能提供一个例子或链接吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-23
    • 2018-09-06
    相关资源
    最近更新 更多