THREE JS RawShaderMaterial - three.js

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...)

Related

How to use own out variable instead of gl_FragColor?

I want to write a simple shader (I am using Three.js with WebGL as shader language) that colors a cube.
here is an image of this cube
It's working as long as I use gl_FragColor in my FragmentShader, but apparently gl_FragColor should not be used anymore as it is deprecated, so I created my own out variable:
in vec3 pos;
out vec4 outColor;
void main() {
float r = pos.x;
float g = pos.y;
float b = pos.z;
outColor = vec4(r,g,b,1.0);
}
However, this results in the following error message:
ERROR: 0:44: 'outColor' : must explicitly specify all locations when using multiple fragment outputs
I looked for possible answers and don't really understand this approach:
layout(location = 0) out vec4 outColor;
This gives me the error message
ERROR: 0:44: 'outColor' : conflicting output locations with previously defined output 'pc_fragColor'
but I never declared pc_fragColor. When I use other numbers than 0 (e.g. layout(location = 1)) then the cube is white.
What am I doing wrong?
I found a solution to my problem. Specifying the GLSLS version in my three.js script when declaring the material helped:
new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vShader,
fragmentShader: fShader,
vertexColors: true,
glslVersion: THREE.GLSL3,
});

Changing fresnel falloff on ThreeJS shader

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;
`

Modified the Phong shader, shadows no longer working properly

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.

Texture repeat with ShaderMaterials in three.js

I have a problem with uniform textures and the repeat of them, because with:
tex.wrapS = THREE.RepeatWrapping;
tex.wrapT = THREE.RepeatWrapping;
tex.repeat.x=100;
tex.repeat.y=100;
it doesn´t work. So I searched the web for a solution and I found the following thread on giuthub:
https://github.com/mrdoob/three.js/issues/787
the threadstarter has the same problem as I do, but unfortunately the links with the answers don´t work anymore.
What do I have to do when I want my uniform textures repeated?
Are you writing your own fragment shader? If so, you'll need to multiply the UV coordinates by the repeat values, e.g.
uniform sampler2D baseTexture;
varying vec2 vUv;
void main()
{
gl_FragColor = texture2D( baseTexture, vUv * 100 );
}
If not, http://stemkoski.github.io/Three.js/Texture-Repeat.html contains an example of texture repeating, e.g.
// texture repeated twice in each direction
var lavaTexture = THREE.ImageUtils.loadTexture( 'images/lava.jpg' );
lavaTexture.wrapS = lavaTexture.wrapT = THREE.RepeatWrapping;
lavaTexture.repeat.set( 2, 2 );
var lavaMaterial = new THREE.MeshBasicMaterial( { map: lavaTexture } );

Threejs Shader - gl_FragColor with Alpha (opacity not changing)

I'm trying to write a simple shader where half of my scene will be displayed and half of the scene will be transparent. I can't seem to figure out why the transparency isn't working:
uniform sampler2D tDiffuse;
varying vec2 vUv;
void main(){
vec2 p = vUv;
vec4 color;
if(p.x < 0.5){
color = (1.0, 0.0, 0.0, 0.0);
}else{
color = texture2D(tDiffuse, p);
}
gl_FragColor = color;
}
The shader is definitely running without errors - the right half of the screen is my threejs scene and the left half of the screen is red (when it should really be transparent). I've read that maybe I need to call glBlendFunc(GL_SRC_ALPHA); - but I am getting errors when I try this. To do this I did renderer.context.blendFuncSeparate(GL_SRC_ALPHA); in my main js file (not the shader). Am I supposed to place this somewhere else to make it work?
Any help would be appreciated. For reference I'm applying my shader with the standard effectsComposer, shaderPass, etc - which most threejs shader examples use.
Thanks in advance for your help!!!
It is difficult to help you with only partial information and code snippets, so I can only make educated guesses.
By default EffectComposer uses a render target with RGB format. Did specify RGBA?
Did you specify material.transparent = true?
three.js r.56
I had this problem and for me it was that the material didn't have transparency enabled.
let myMaterial = new THREE.ShaderMaterial({
uniforms: myUniforms,
fragmentShader: myFragmentShader(),
vertexShader: myVertexShader(),
});
myMaterial.transparent = true;

Resources