So I spent quite a bit of time writing my own simple Phong shader before figuring that there must be some way to modify the existing three.js Phong shaders to suit my needs. What I ended up doing looked like this
var phongShader = THREE.ShaderLib.phong;
var phongMatUniforms = THREE.UniformsUtils.clone(phongShader.uniforms);
var uniforms = phongMatUniforms;
uniforms.currentTime = {
type : "f",
value : 1.0
};
uniforms.hourlyValues = {
type : "fv1",
value : hourlyValuesArray
}
Now that I had access to the Phong vertex and fragment shader, I went poking through the shader and modified the section that I thought would achieve what I was looking for :
//original Three.js phong shader line
mvPosition = modelViewMatrix * vec4( position, 1.0 );
//my edited / updated line
mvPosition = modelViewMatrix * vec4( position.x, position.y, position.z + interpolatedValue, 1.0 );
Great! Everything worked just like I thought it should. Then I decided to try and add shadows with a spotlight. The good news was that shadows worked but they didn't work with the geometry that the shader had modified. Here's a screenshot example:
http://imgur.com/8Sdxq60
So I figured that there must be somewhere else in the Phong vertex shader that needs another simple update. The problem is, I can't find it. It looked to me like this:
#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )
//original shader code
vec4 worldPosition = modelMatrix * vec4( position, 1.0 );
//updated to account for distorted geometry
worldPosition = modelMatrix * vec4( position.x, position.y, position.z + interpolatedValue, 1.0 );
#endif
Should do the trick but all it did was produce the screenshot above.
I'm kind of at a loss here as to what / where in the shader I should be modifying to take into account my distorted geometry. It looks like this bit at the end:
#ifdef USE_SHADOWMAP
for( int i = 0; i < MAX_SHADOWS; i ++ ) {
vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;
}
#endif
Sets the shadow varyings that I need, but updating "worldPosition" like I did should fix that, no?
Ok, so I found the relavant shaders for the shadow casting. They do reside in THREE.ShadowMapPlugin. Specifically there is a line :
if ( object.customDepthMaterial ) {
material = object.customDepthMaterial;
}
So you assign a material to the objects.customDepthMaterial.
I used:
var depthShader = THREE.ShaderLib[ "depthRGBA" ];
var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms );
depthUniforms.currentTime = {
type : "f",
value : 1.0
};
depthUniforms.hourlyValues = {
type : "fv1",
value : hourlyValuesArray
}
As the basis for the depthmaterial and then assigned it to the object thusly:
extrudedMesh.customDepthMaterial = depthMaterial;
Worked like a charm. You can then update the uniforms for the depth material just like you would for any other material.
Related
I've been making use of this shader inside of my ThreeJS project, except I've more or less copied the code verbatim because I have no idea how to write a shader function. Basically I want to edit the rate of falloff on the fresnel effect so that it's only really the edges that are using the colour with a slight glow coming inside
var material = THREE.extendMaterial(THREE.MeshStandardMaterial, {
// Will be prepended to vertex and fragment code
header: 'varying vec3 vNN; varying vec3 vEye;',
fragmentHeader: 'uniform vec3 fresnelColor;',
// Insert code lines by hinting at a existing
vertex: {
// Inserts the line after #include <fog_vertex>
'#include <fog_vertex>': `
mat4 LM = modelMatrix;
LM[2][3] = 0.0;
LM[3][0] = 0.0;
LM[3][1] = 0.0;
LM[3][2] = 0.0;
vec4 GN = LM * vec4(objectNormal.xyz, 1.0);
vNN = normalize(GN.xyz);
vEye = normalize(GN.xyz-cameraPosition);`
},
fragment: {
'gl_FragColor = vec4( outgoingLight, diffuseColor.a );' :
`gl_FragColor.rgb += ( 1.0 - -min(dot(vEye, normalize(vNN) ), 0.0) ) * fresnelColor;`
},
// Uniforms (will be applied to existing or added)
uniforms: {
diffuse: new THREE.Color( 'black' ),
fresnelColor: new THREE.Color( 'blue' )
}
});
I've tried changing the number in this line gl_FragColor.rgb += ( **1.0** - -min(dot(vEye, normalize(vNN) ), 0.0) ) * fresnelColor; and whilst that did stop the gradient of the fresnel, it was a hard stop, as though it was limiting levels instead of the rate of gradient.
I just need help with how I can make the fall off not as far into my models so that it's only really the edges that have it
Maybe this will help:
fragment: {
'gl_FragColor = vec4( outgoingLight, diffuseColor.a );' : `
float m = ( 1.0 - -min(dot(vEye, normalize(vNN)), 0.0) );
m = pow(m, 8.); // the greater the second parameter, the thinner effect you get
gl_FragColor.rgb += m * fresnelColor;
`
I have this code:
vec4 localPosition = vec4( position, 1.);
vec4 worldPosition = modelMatrix * localPosition;
vec3 look = normalize( vec3(cameraPosition) - vec3(worldPosition) );
vec3 transformed = vec3( position ) + look;
But for some reason, it just moves the vertex 1 unit towards the origin point in the scene (0,0,0).
I need it to move the vertex towards the camera(where you are viewing the scene from).
I can't seem to find clear information anywhere on how to accomplish this.
It was a three.js issue.. Had to set the isShaderMaterial = true, in order to get the cameraPosition to update. o_o
material.isShaderMaterial = true; //We need to set this so that the cameraPosition uniform is updated in the shader
material.onBeforeCompile = function ( shader ) {
shader.vertexShader = shader.vertexShader.replace(
'#include <begin_vertex>',
[
'float myOffset = 0.0;',
'myOffset = (vColor.r + vColor.g + vColor.b) < 3.0 ? 0.01 : 0.0;',
'vec4 localPosition = vec4( position, 1.);',
'vec4 worldPosition = modelMatrix * localPosition;',
'vec3 look = myOffset * normalize( cameraPosition - vec3(worldPosition) );',
'vec3 transformed = vec3( position ) + look;'
].join( '\n' )
);
material.userData.shader = shader;
};
if you have a view matrix, transform the vertex position to view coordinate and then you can do transformation according to the camera axis.
I'm creating an animation using envmap.
Everything works as expected but instead of using 6 images inside a CubeTexture, I would like to use 6 animated textures.
I thought it would be obvious but I'm on it for hours now, I searched a lot everywhere I could but it's like nobody on earth ever used an animated texture as envMap...
I found some interesting posts concerning SphericalMappingReflection based on a single texture - exactly what I am looking for I think - , but impossible to find any example of use...
I also checked the Three.js shader source code in order to understand how envMap is done and I found theses lines
#elseENVMAP_TYPE_EQUIREC
float flipNormal = 1.0;
#endif
#ifdef ENVMAP_TYPE_CUBE
vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );
#elif defined( )
vec2 sampleUV;
sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );
sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;
vec4 envColor = texture2D( envMap, sampleUV );
#elif defined( ENVMAP_TYPE_SPHERE )
vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));
vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );
#endif
Then it looks possible to create an envMap using texture2D instead of textureCube, but I didn't find any hint on how to define "ENVMAP_TYPE_EQUIREC" or "ENVMAP_TYPE_SPHERE"
Can you help me please ?
I feel a bit depressed with my problem and really don't know how to do.
Thank you !
( please excuse me if my english is not perfect, it's not my native language )
I'm trying to build my own shaders using RawShaderMaterial. Right now I've created a basic lighting setup which got me thinking if I was to later on build a scene using the standard THREE.JS lights and I wanted those lights to interact with the custom shaders I built then how would I go about doing that?
take look at WebGLProgram
your raw shader will be taken, parsed and modified
some highlights about how to make use of lights :
make sure you have lights enabled when creating your material
material = new THREE.RawShaderMaterial({
uniforms: uniforms,
vertexShader: vertex,
fragmentShader: fragment,
lights: true
});
you can get the number of lights of each type in scene by including one of special strings inside of your shader code:
string.replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights )
.replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights )
.replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights )
.replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights );
take a look at WebGLLights you will be able to acces the lights through uniforms
for example a bit of a fragment shader pseudo code with directional lights (taken from phong material code)
#if NUM_DIR_LIGHTS > 0
struct DirectionalLight {
vec3 direction;
vec3 color;
int shadow;
float shadowBias;
float shadowRadius;
vec2 shadowMapSize;
};
uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];
#endif
main(){
.
.
GeometricContext geometry;
geometry.position = - vViewPosition;
geometry.normal = normal;
geometry.viewDir = normalize( vViewPosition );
IncidentLight directLight;
#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )
DirectionalLight directionalLight;
for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
directionalLight = directionalLights[ i ];
directLight = getDirectionalDirectLightIrradiance( directionalLight, geometry );
RE_Direct( directLight, geometry, material, reflectedLight );
}
#endif
it is not complete but it should illustrate the main points, you can always create a material for example MeshPhong, add it to scene and look at the vertex and fragment code it compiled from(or catch it inside WebGLProgram as it is beign parsed for a more readable version as it unrolls loops and replaces the light nums...)
looking for info on how to recreate the ShaderToy parameters iGlobalTime, iChannel etc within threejs. I know that iGlobalTime is the time elapsed since the Shader started, and I think the iChannel stuff is for pulling rgb out of textures, but would appreciate info on how to set these.
edit: have been going through all the shaders that come with three.js examples and think that the answers are all in there somewhere - just have to find the equivalent to e.g. iChannel1 = a texture input etc.
I am not sure if you have answered your question, but it might be good for others to know the integration steps for shadertoys to THREEJS.
First, you need to know that shadertoys is a fragment shaders. That being said, you have to set a "general purpose" vertex shader that should work with all shadertoys (fragment shaders).
Step 1
Create a "general purpose" vertex shader
varying vec2 vUv;
void main()
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
This vertex shader is pretty basic. Notice that we defined a varying variable vUv to tell the fragment shader where is the texture mapping. This is important because we are not going to use the screen resolution (iResolution) for our base rendering. We will use the texture coordinates instead. We have done that in order to integrate multiple shadertoys on different objects in the same THREEJS scene.
Step 2
Pick the shadertoys that we want and create the fragment shader. (I have chosen a simple toy that performs well: Simple tunnel 2D by niklashuss).
Here is the given code for this toy:
void main(void)
{
vec2 p = gl_FragCoord.xy / iResolution.xy;
vec2 q = p - vec2(0.5, 0.5);
q.x += sin(iGlobalTime* 0.6) * 0.2;
q.y += cos(iGlobalTime* 0.4) * 0.3;
float len = length(q);
float a = atan(q.y, q.x) + iGlobalTime * 0.3;
float b = atan(q.y, q.x) + iGlobalTime * 0.3;
float r1 = 0.3 / len + iGlobalTime * 0.5;
float r2 = 0.2 / len + iGlobalTime * 0.5;
float m = (1.0 + sin(iGlobalTime * 0.5)) / 2.0;
vec4 tex1 = texture2D(iChannel0, vec2(a + 0.1 / len, r1 ));
vec4 tex2 = texture2D(iChannel1, vec2(b + 0.1 / len, r2 ));
vec3 col = vec3(mix(tex1, tex2, m));
gl_FragColor = vec4(col * len * 1.5, 1.0);
}
Step 3
Customize the shadertoy raw code to have a complete GLSL fragment shader.
The first thing missing out the code are the uniforms and varyings declaration. Add them at the top of your frag shader file (just copy and paste the following):
uniform float iGlobalTime;
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
varying vec2 vUv;
Note, only the shadertoys variables used for that sample are declared, plus the varying vUv previously declared in our vertex shader.
The last thing we have to twick is the proper UV mapping, now that we have decided to not use the screen resolution. To do so, just replace the line that uses the IResolution uniforms i.e.:
vec2 p = gl_FragCoord.xy / iResolution.xy;
with:
vec2 p = -1.0 + 2.0 *vUv;
That's it, your shaders are now ready for usage in your THREEJS scenes.
Step 4
Your THREEJS code:
Set up uniform:
var tuniform = {
iGlobalTime: { type: 'f', value: 0.1 },
iChannel0: { type: 't', value: THREE.ImageUtils.loadTexture( 'textures/tex07.jpg') },
iChannel1: { type: 't', value: THREE.ImageUtils.loadTexture( 'textures/infi.jpg' ) },
};
Make sure the textures are wrapping:
tuniform.iChannel0.value.wrapS = tuniform.iChannel0.value.wrapT = THREE.RepeatWrapping;
tuniform.iChannel1.value.wrapS = tuniform.iChannel1.value.wrapT = THREE.RepeatWrapping;
Create the material with your shaders and add it to a planegeometry. The planegeometry() will simulate the shadertoys 700x394 screen resolution, in other words it will best transfer the work the artist intented to share.
var mat = new THREE.ShaderMaterial( {
uniforms: tuniform,
vertexShader: vshader,
fragmentShader: fshader,
side:THREE.DoubleSide
} );
var tobject = new THREE.Mesh( new THREE.PlaneGeometry(700, 394,1,1), mat);
Finally, add the delta of the THREE.Clock() to iGlobalTime value and not the total time in your update function.
tuniform.iGlobalTime.value += clock.getDelta();
That is it, you are now able to run most of the shadertoys with this setup...
2022 edit: The version of Shaderfrog described below is no longer being actively developed. There are bugs in the compiler used making it not able to parse all shaders correctly for import, and it doesn't support many of Shadertoy's features, like multiple image buffers. I'm working on a new tool if you want to follow along, otherwise you can try the following method, but it likely won't work most of the time.
Original answer follows:
This is an old thread, but there's now an automated way to do this. Simply go to http://shaderfrog.com/app/editor/new and on the top right click "Import > ShaderToy" and paste in the URL. If it's not public you can paste in the raw source code. Then you can save the shader (requires sign up, no email confirm), and click "Export > Three.js".
You might need to tweak the parameters a little after import, but I hope to have this improved over time. For example, ShaderFrog doesn't support audio nor video inputs yet, but you can preview them with images instead.
Proof of concept:
ShaderToy https://www.shadertoy.com/view/MslGWN
ShaderFrog http://shaderfrog.com/app/view/247
Full disclosure: I am the author of this tool which I launched last week. I think this is a useful feature.
This is based on various sources , including the answer of #INF1.
Basically you insert missing uniform variables from Shadertoy (iGlobalTime etc, see this list: https://www.shadertoy.com/howto) into the fragment shader, the you rename mainImage(out vec4 z, in vec2 w) to main(), and then you change z in the source code to 'gl_FragColor'. In most Shadertoys 'z' is 'fragColor'.
I did this for two cool shaders from this guy (https://www.shadertoy.com/user/guil) but unfortunately I didn't get the marble example to work (https://www.shadertoy.com/view/MtX3Ws).
A working jsFiddle is here: https://jsfiddle.net/dirkk0/zt9dhvqx/
Change the shader from frag1 to frag2 in line 56 to see both examples.
And don't 'Tidy' in jsFiddle - it breaks the shaders.
EDIT:
https://medium.com/#dirkk/converting-shaders-from-shadertoy-to-threejs-fe17480ed5c6