How might I make a gradient color for the sky without using images?
This is what I have so far (I know why it doesn't work - it is only reference for what I am trying to do):
<a-sky color="linear-gradient(red, yellow)"></a-sky>
ThreeJS solutions welcome as well, but I would need help integrating them into an A-Frame scene.
I ended up solving it with help from the given examples. Thank you Marko, Pablieros, ngokevin, and Mr Doob!
I ended up creating a custom shader and a primitive element to go along with it:
<a-gradient-sky material="topColor:0 1 0; bottomColor:1 0 0;"></a-gradient-sky>
AFRAME.registerShader('gradient', {
schema: {
topColor: {type: 'vec3', default: '1 0 0', is: 'uniform'},
bottomColor: {type: 'vec3', default: '0 0 1', is: 'uniform'},
offset: {type: 'float', default: '400', is: 'uniform'},
exponent: {type: 'float', default: '0.6', is: 'uniform'}
},
vertexShader: [
'varying vec3 vWorldPosition;',
'void main() {',
'vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
'vWorldPosition = worldPosition.xyz;',
'gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0 );',
'}'
].join('\n'),
fragmentShader: [
'uniform vec3 bottomColor;',
'uniform vec3 topColor;',
'uniform float offset;',
'uniform float exponent;',
'varying vec3 vWorldPosition;',
'void main() {',
' float h = normalize( vWorldPosition + offset ).y;',
' gl_FragColor = vec4( mix( bottomColor, topColor, max( pow( max(h, 0.0 ), exponent ), 0.0 ) ), 1.0 );',
'}'
].join('\n')
});
AFRAME.registerPrimitive('a-gradient-sky', {
defaultComponents: {
geometry: {
primitive: 'sphere',
radius: 5000,
segmentsWidth: 64,
segmentsHeight: 20
},
material: {
shader: 'gradient'
},
scale: '-1 1 1'
},
mappings: {
topColor: 'material.topColor',
bottomColor: 'material.bottomColor',
offset: 'material.offset',
exponent: 'material.exponent'
}
});
You may want to Register a custom material/shader similar to #ngokevin 's aframe-sun-sky at ngokevin/kframe or use registerShader
Which would look something like following example from docs.
AFRAME.registerShader('hello-world-shader', {
schema: {
color: { type: 'vec3', default: '0.5 0.5 0.5', is: 'uniform' }
},
vertexShader: [
'void main() {',
' gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;',
'}'
].join('\n'),
fragmentShader: [
'uniform vec3 color;'
'void main() {'
' gl_FragColor = vec4(color, 1.0);',
'}'
].join('\n')
});
And just for notice you tile says Skybox by aframe primitive <a-sky> is geometry: { primitive: 'sphere' }
Related
I want to do a sort of "inner glow", where a partially transparent color is more intense towards the outside of a sphere, as the user looks at it.
In plain ol' CSS it would be pretty simple:
.gradient{
height: 500px;
width: 500px;
border-radius: 50%;
box-shadow:0 0 50px 1px inset rgba(0,225,255,0.8);
background: rgba(0,225,255,0.1);
}
<div class="gradient"></div>
This tutorial has this effect, and I have a separate question here about an issue I ran into implementing if anyone else has trouble.
Some custom GL shaders: this is GL code that lives in a string to get compiled later:
const vertexShader = `
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main()
{
vec3 vNormal = normalize( normalMatrix * normal );
vec3 vNormel = normalize( normalMatrix * viewVector );
intensity = pow( c - dot(vNormal, vNormel), p );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`;
const fragmentShader = `
uniform vec3 glowColor;
varying float intensity;
void main()
{
vec3 glow = glowColor * intensity;
gl_FragColor = vec4( glow, 1.0 );
}`;
This is the material to create the effect. Play with c and p values for intensity/depth of the "gradient".
const customMaterial = new THREE.ShaderMaterial({
uniforms: {
"c": { type: "f", value: 1.5 },
"p": { type: "f", value: 2 },
glowColor: { type: "c", value: new THREE.Color(0x00e1ff) },
viewVector: { type: "v3", value: camera.position }
},
vertexShader: vertexShader,
fragmentShader: fragmentShader,
side: THREE.FrontSide,
blending: THREE.AdditiveBlending,
transparent: true
});
You'll want to apply it to your geometry, and scalar your geometry up to the size of "shell" you desire.
const moonGlow = new THREE.Mesh( Orb, customMaterial);
moonGlow.position = moon.position;
moonGlow.scale.multiplyScalar(1.2);
scene.add( moonGlow );
Finally, facing is important. In you're changing up positions, like in an animation loop with motion controls, add this:
customMaterial.uniforms.viewVector.value = camera.position;
I'm very new to shaders. I'm trying to achieve this color and transparent effect:
Example #1 Example#2
This is my result:
my result
This what I have so far:
this.material = new THREE.ShaderMaterial({
extensions: {
derivatives: "#extension GL_OES_standard_derivatives : enable"
},
side: THREE.DoubleSide,
uniforms: {
time: { value: 0 },
resolution: { value: new THREE.Vector4() }
},
transparent: true,
vertexShader: vertex,
fragmentShader: fragment
});
fragmentShader
varying vec2 vUv;
varying float vNoise;
uniform vec2 u_resolution;
void main() {
vec3 color1 = vec3(0.,0.,0.);
vec3 color2 = vec3(1.,1.,1.);
vec3 finalcolor = mix(color1,color2,0.9*(vNoise+1.));
gl_FragColor = vec4( vec3(finalcolor),0.2);
}
How would you go about this?
Thanks!
I guess that it is a bit late for answer, but maybe someone will have same problem. I think you are looking for Fresnel Shader:
Three.js example can be found here:
https://jsfiddle.net/8n36c47p/4/
Here is main shader part:
vertexShader: [
"varying vec3 vPositionW;",
"varying vec3 vNormalW;",
"void main() {",
" vPositionW = vec3( vec4( position, 1.0 ) * modelMatrix);",
" vNormalW = normalize( vec3( vec4( normal, 0.0 ) * modelMatrix ) );",
" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join( "\n" ),
fragmentShader: [
"varying vec3 vPositionW;",
"varying vec3 vNormalW;",
"void main() {",
" vec3 color = vec3(1., 1., 1.);",
" vec3 viewDirectionW = normalize(cameraPosition - vPositionW);",
" float fresnelTerm = dot(viewDirectionW, vNormalW);",
" fresnelTerm = clamp(1.0 - fresnelTerm, 0., 1.);",
" gl_FragColor = vec4( color * fresnelTerm, 1.);",
"}"
].join( "\n" )
I'm using this shader to chromakey a video. I would like to add opacity to the same video. I've been trying to add the opacity component to the shader but something more must be missing (don't know what).
I've added opacity in the schema:
opacity: {type: 'number', is: 'uniform', default: 0.5}
And in the update function:
this.material.uniforms.opacity = data.opacity
Here's all the shader code:
AFRAME.registerShader('chromakey', {
schema: {
src: {type: 'map'},
color: {default: {x: 0.1, y: 0.9, z: 0.2}, type: 'vec3', is: 'uniform'},
transparent: {default: true, is: 'uniform'},
opacity: {type: 'number', is: 'uniform', default: 0.5}
},
init: function (data) {
var videoTexture = new THREE.VideoTexture(data.src)
videoTexture.minFilter = THREE.LinearFilter
this.material = new THREE.ShaderMaterial({
uniforms: {
color: {
type: 'c',
value: data.color
},
texture: {
type: 't',
value: videoTexture
}
},
vertexShader: this.vertexShader,
fragmentShader: this.fragmentShader
})
},
update: function (data) {
this.material.color = data.color
this.material.src = data.src
this.material.transparent = data.transparent
this.material.uniforms.opacity = data.opacity
},
vertexShader: [
'varying vec2 vUv;',
'void main(void)',
'{',
'vUv = uv;',
'vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
'gl_Position = projectionMatrix * mvPosition;',
'}'
].join('\n'),
fragmentShader: [
'uniform sampler2D texture;',
'uniform vec3 color;',
'varying vec2 vUv;',
'void main(void)',
'{',
'vec3 tColor = texture2D( texture, vUv ).rgb;',
'float a = (length(tColor - color) - 0.5) * 7.0;',
'gl_FragColor = vec4(tColor, a);',
'}'
].join('\n')
})
When you update the value of the uniform (function update), the you have to set the value property and you have to set the notification, that the uniforms of the material needs to be updated (needsUpdate):
this.material.uniforms.opacity.value = data.opacity
this.material.needsUpdate = true
Further you have to add the uniform opacity to the fragment shader. See the following fragment shader which multiplies the opacity uniform to the alpha channel:
uniform float opacity;
uniform sampler2D texture;
uniform vec3 color;
varying vec2 vUv;
void main(void)
{
vec3 tColor = texture2D( texture, vUv ).rgb;
float a = (length(tColor - color) - 0.5) * 7.0;
gl_FragColor = vec4(tColor, a * opacity);
}
Note, if opacity is set to 0.0, the output will disappear completely.
You would need to add an opacity uniform to the shader in order for it to support what you want.
Try the following code (I haven't tested it) and see if it behaves how you want
fragmentShader: [
'uniform sampler2D texture;',
'uniform vec3 color;',
'uniform float opacity;', // add the uniform to the shader
'varying vec2 vUv;',
'void main(void)',
'{',
'vec3 tColor = texture2D( texture, vUv ).rgb;',
'float a = (length(tColor - color) - 0.5) * 7.0;',
'gl_FragColor = vec4(tColor, a*opacity);', // add the opacity multiplier to the alpha channel
'}'
].join('\n')
I have one problem here)
I decided to use my shader for (scene.overrideMaterial) and I need to somehow get the map texture.
MaterialsShader = {
uniforms: {
time: { type: 'f', value: 0.0 },
},
vertexShader:
"varying vec2 vUv; \n\
void main(){\n\
vUv = uv;\n\
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );\n\
gl_Position = projectionMatrix * mvPosition;\n\
}",
fragmentShader: [
'varying vec2 vUv;',
'uniform float opacity;',
'#ifdef USE_MAP',
'uniform sampler2D map;',
'#endif',
'void main(){',
'vec3 color = vec3(1.0,0.0,0.0) * opacity;',
'#ifdef USE_MAP',
'vec4 mapTexel = texture2D( map, vUv.xy );',
'gl_FragColor = mapTexel;',
'#endif',
//'gl_FragColor.rgb = color;',
'}'].join("\n")
}
But for some reason the texture itself does not exist, I get black material.
var uniforms = THREE.UniformsUtils.clone( MaterialsShader.uniforms );
uniforms = THREE.UniformsUtils.merge( [uniforms, THREE.UniformsLib['common'],THREE.UniformsLib['lights']] );
var material = new THREE.ShaderMaterial({
uniforms : uniforms,
vertexShader : MaterialsShader.vertexShader,
fragmentShader : MaterialsShader.fragmentShader,
});
How can I get a (map) texture?
1) You need to actually define your map uniform...
MaterialsShader = {
uniforms: {
time: { type: 'f', value: 0.0 },
map: { type: 't', value: yourLoadedTexture },
},
2) You need to define USE_MAP in order to enter that code segment of your shader. Either add this to the top of your shader code:
#define USE_MAP true
Or you can set a defines property for your shader:
var material = new THREE.ShaderMaterial({
uniforms : uniforms,
vertexShader : MaterialsShader.vertexShader,
fragmentShader : MaterialsShader.fragmentShader,
map : yourLoadedTexture,
defines : {
USE_MAP: true
}
});
These have the same effect.
I'm having troubles enabling scene Fog in THREE.ShaderMaterial. Currently the fog only affects other geometries, but the Skydome that is created using THREE.ShaderMaterial, is unaffected by the fog.
There appears to be a boolean fog parameter in ShaderMaterial, which apparently should be set to true to use scene fog. Using it results in uniforms.fogColor is undefined errors however. The error happens at WebGLRenderer function refreshUniformsFog.
Is it a bug or am I using the parameter wrong?
Test case based on webgl_materials_lightmap.html example here: http://jsfiddle.net/HXhb4/
If you set fog to true in line 62 and run the test, you get the errors. What I would like to happen is the skydome to be affected by the fog like a model or skydome created with normal MeshPhongMaterial.
If you want to use fog with your custom ShaderMaterial, you need to be sure to specify the required fog uniforms. For example,
var uniforms = {
topColor: { type: "c", value: new THREE.Color( 0x0077ff ) },
bottomColor: { type: "c", value: new THREE.Color( 0xffffff ) },
offset: { type: "f", value: 33 },
exponent: { type: "f", value: 0.6 },
fogColor: { type: "c", value: scene.fog.color },
fogNear: { type: "f", value: scene.fog.near },
fogFar: { type: "f", value: scene.fog.far }
}
var skyMat = new THREE.ShaderMaterial( {
vertexShader: vertexShader,
fragmentShader: fragmentShader,
uniforms: uniforms,
side: THREE.BackSide,
fog: true
} );
Also specify fogDensity if you decide to use it. You will also have to incorporate the fog logic into your shader.
three.js r.59
In the accepted answer WestLangley mentioned:
You will also have to incorporate the fog logic into your shader.
I figured out how to do that by reading the example NormalDisplacementShader.js. Here are the steps:
Write your shader in an external .js file
Add the UniformsLib
Add the ShaderChunks
In the fragment shader, declare vec3 outgoingLight
Use outgoingLight in your output gl_FragColor
Example below:
THREE.YourCustomShader = {
uniforms: THREE.UniformsUtils.merge( [
THREE.UniformsLib[ "fog" ],
{
"someCustomUniform" : { type: 'f', value: 1.0 }
}
] ),
fragmentShader: [
"varying float someCustomVarying;",
THREE.ShaderChunk[ "common" ],
THREE.ShaderChunk[ "fog_pars_fragment" ],
"void main() {",
"vec3 outgoingLight = vec3( 0.0 );",
THREE.ShaderChunk[ "fog_fragment" ],
"gl_FragColor = vec4(outgoingLight, 1.0);",
"}"
].join("\n"),
vertexShader: [
"uniform float someCustomUniform;",
"varying float someCustomVarying;",
"void main() {",
"someCustomVarying = 1.0 * someCustomUniform;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0 );",
"}"
].join("\n")
};