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.
Demo code below. Three files index.html
, sky.js
, and earth.js
.
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.
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.
The animation seems pretty slow (5 fps in my testing), I don't know why. The official fragment shader example is the same way.
You'll need to provide your own textures worldDay.png
and worldNight.png
. For testing I used images on this site.
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.
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;
}
`
});
In very rough terms (because it is a lot of work if you are newbie in 3D):
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With