8

Is there a way to set a special texture for illuminated object parts in aframe? For example I want to set a night texture for earth where one side that isn't illuminated has another texture.

Edit: It seems I need some kind of shader here but I can't find anything useful for this problem.

Edit 2: The shader has to be flexible and has to work with different types of lighting sources and on objects that aren't a perfect sphere.

Niko Lang
  • 847
  • 2
  • 9
  • 17
  • Do you specifically want the amount of incoming light to determine which texture appears? Or is simply determining which texture to use based on the _direction_ each part of the object is facing enough? – Don McCurdy May 01 '18 at 01:18
  • First. I want to change the texture depending on the amount of light. The direction alone wouldn't be enough. – Niko Lang May 01 '18 at 21:20
  • Interesting question – Pasha May 06 '18 at 16:33

3 Answers3

3

Demo code below. Three files index.html, sky.js, and earth.js.

  1. Mostly stitched together from two samples for loading textures and fragment shaders. The main contributions are:

    1-1. Lines worldDayTex: { type: 'map', is: 'uniform' } and uniform sampler2D worldDayTex; in the shader earth.js. Here, the WebGL shader declares a texture map uniform for input.

    1-2. Line skyEl.setAttribute('material', 'worldDayTex', '#worldDay' ); in index.html. Here, the texture map is input from HTML->WebGL and assigned to the uniform.

  2. Shading the earth from day to night, you probably want to know how much sunlight falls on a given point on earth. Can we safely assume the sun is your only source of light? If so, it's a simple equation of the earth's surface normal at each given point, with the direction of the sun. Accordingly, this example isn't based on sampling light intensity per se, but only an equation between the two vectors.

  3. The animation seems pretty slow (5 fps in my testing), I don't know why. The official fragment shader example is the same way.

  4. You'll need to provide your own textures worldDay.png and worldNight.png. For testing I used images on this site.

  5. Probably obvious, but you'll need to patch the path to aframe-master.min.js before using this, or download it to the same directory for testing.

  6. Tested in Firefox version 59, 64-bit, on Windows. Chrome failed to support the A-Frame texture handling, in my testing.

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;
    }
    `
});
MichaelsonBritt
  • 976
  • 6
  • 9
  • Good approach but I need a more flexible solution because I have more than one light source and need to use it with a spotlight. – Niko Lang May 12 '18 at 20:58
  • 1
    The calculation for a spot light would be similar but more complex. Take the light position in space, subtract the surface position at the shade point, normalize that difference vector, and that's the unit vector from the light to the shade point. The angle between that vector and the unit vector direction in which the light is pointing indicates whether the shade point is within the spot cone of the light. Also, shaders often have a fixed limit of the number of allowable lights, like eight. They calculate each and add up the lighting results. – MichaelsonBritt May 12 '18 at 21:11
  • Also, if you would like an equation for lighting in a spotlight shader, or if you have other questions about lighting in shaders, please post a separate question, and I can provide a more complete answer there. – MichaelsonBritt May 13 '18 at 11:06
2

In very rough terms (because it is a lot of work if you are newbie in 3D):

  1. Create 2 earth textures. 1 full day and 1 full night.
  2. Create a fragment shader component that receives the 2 textures and sun light position.
  3. Use the dot product betwen light and earth pixel position to determine if the pixel is under the sun or not.
  4. If under the sun, sample the day texture pixel and put it into render target; if not, sample the night texture pixel and put it into render target;
jlvaquero
  • 8,571
  • 1
  • 29
  • 45
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>
Bhaskar Reddy
  • 171
  • 1
  • 9
  • Can you specify what part? The objects doesn't do much except casting shadows but as long as there is no light source they don't do anything. But I don't want to cast a shadow. I want to change the texture depending on the illumination of the object. – Niko Lang Apr 29 '18 at 20:43
  • @Niko Lang do u mean Chiral effect texture then try to add Chiral image – Bhaskar Reddy Apr 29 '18 at 20:52
  • I cant find anything useful with that term. Could you provide an example or link? – Niko Lang Apr 29 '18 at 21:11