Текстура для освещенных объектов в рамке - PullRequest
0 голосов
/ 27 апреля 2018

Есть ли способ установить специальную текстуру для подсвеченных частей объекта в кадре? Например, я хочу установить ночную текстуру для земли, где одна сторона, которая не освещена, имеет другую текстуру.

Редактировать: Кажется, мне нужен какой-то шейдер, но я не могу найти ничего полезного для этой проблемы.

Редактировать 2: шейдер должен быть гибким и работать с различными типами источников освещения и с объектами, которые не являются идеальной сферой.

Ответы [ 3 ]

0 голосов
/ 30 апреля 2018

В очень грубых выражениях (потому что если вы новичок в 3D, много работы):

  1. Создание 2 текстур земли. 1 полный день и 1 полная ночь.
  2. Создание компонента фрагмента шейдера, который получает 2 текстуры и положение солнечного света.
  3. Используйте точечное произведение между положением пикселя света и земли, чтобы определить, находится ли пиксель под солнцем или нет.
  4. Находясь под солнцем, возьмите пиксель текстуры дня и поместите его в цель рендеринга; если нет, то сэмплируйте ночной пиксель текстуры и поместите его в цель рендеринга;
0 голосов
/ 06 мая 2018

Демо-код ниже. Три файла index.html, sky.js и earth.js.

  1. В основном сшитые из двух образцов для загрузки текстур и фрагментных шейдеров . Основные вклады:

    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 кадров в секунду в моем тестировании), я не знаю почему. Официальный пример фрагмента шейдера аналогичен.

  4. Вам нужно будет предоставить свои собственные текстуры worldDay.png и worldNight.png. Для тестирования я использовал изображения на этом сайте .

  5. Вероятно, очевидно, но вам нужно будет исправить путь к aframe-master.min.js перед использованием или загрузить его в тот же каталог для тестирования.

  6. Протестировано в Firefox версии 59, 64-битная, в Windows В моем тестировании 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;
    }
    `
});
0 голосов
/ 29 апреля 2018
 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>
...