Using/extending the built-in lambert shader in THREE.ShaderMaterial - three.js

With three.js I've been trying to create a ShaderMaterial that uses the built-in lambert shader, so I can extend the lambert shader and add my own shader code, for instance do some simple waves in the vertex shader, without having to rewrite all the handy lighting code that's already in place.
My code is as follows:
var lambertShader = THREE.ShaderLib['lambert'];
lambertShader.uniforms.map = {type:"t", value:this.waterTexture};
this.seaMaterial = new THREE.ShaderMaterial({
vertexShader: "#define USE_MAP\n"+lambertShader.vertexShader,
fragmentShader: "#define USE_MAP\n"+lambertShader.fragmentShader,
uniforms: lambertShader.uniforms,
lights: true,
transparent: true,
opacity: 0.8,
depthTest: false,
depthWrite: false
});
First, I copy the default shader program and uniforms from THREE.ShaderLib['lambert'].
Then I set the 'map' uniform to the texture I want to use.
Lastly I create the ShaderMaterial using the lambert shader program.
Problems I am having:
I have to prefix the vertex and fragment shaders with "#define USE_MAP\n" to let the shader program know it should use the texture map. If I don't add this the texture map isn't used. Adding 'map: true' does not fix this as suggested in a different StackOverflow thread.
depthTest and depthWrite are disabled but have no effect. When using a normal MeshLambertMaterial this works just fine.
The texture doesn't repeat correctly like when I use a normal MeshLambertMaterial. (I've set it to repeat 12 times horizontally and 12 times vertically)
I get the feeling there's more #define preprocessor directives that are automatically enabled for MeshLambertMaterials but aren't for ShaderMaterials, or it might be a different problem.
It appears that lighting works correctly though, so at least that's getting sent to the shader.
Is there a way to automatically add the needed preprocessor directives or in any other way fixing the depthTest/depthWrite and texture repeat problems? (because I think that's why the shader doesn't function like it should)
I've looked around in the three.js source and it appears that in WebGLProgram.js, starting from line 127 there is some code that adds the necessary #define statements to a shader program, but I'm not sure why the code isn't called for THREE.ShaderMaterial.
Thanks in advance,
Robin

Related

transforming a shader pass to a shader material

I would like to transform the shader pass below to a shader material.
https://github.com/felixturner/bad-tv-shader
Is it possible, how to procede ? For me it should be easy if the shaderMaterial is applied to a plane Geometry.
I tried this transformation on this codesandbox : https://codesandbox.io/s/r3f-wavey-image-shader-forked-4fb238
The only thing I did for the moment is copying the code of the shader pass into a object.
Thanks.

ThreeJS Points (Point Cloud) with Lighting using custom Shader Material

Coded using:
Using ThreeJS v0.130.1
Framework: Angular 12, but that's not relevant to the issue.
Testing on Chrome browser.
I am building an application that gets more than 100K points. I use these points to render a THREE.Points object on the screen.
I found that default THREE.PointsMaterial does not support lighting (the points are visible with or without adding lights to the scene).
So I tried to implement a custom ShaderMaterial. But I could not find a way to add lighting to the rendered object.
Here is a sample of what my code is doing:
Sample App on StackBlitz showing my current attempt
In this code, I am using sample values for point cloud data, normals and color but everything else is similar to my actual application. I can see the 3D object, but need more proper lighting using normals.
I need help or guidance to implement the following:
Add lighting to custom shader material. I have Googled and tried many things, no success so far.
Using normals, show the effects of lighting (In this sample code, the normals are fixed to Y-axis direction, but I am calculating them based on some vector logic in actual application). So calculating normals is already done, but I want to use them to show light shine/shading effect in the custom shader material.
And in this sample, color attribute is set to fixed red color, but in actual application I am able to apply colors using UV range from a texture to color attribute.
Please advise how/if I can get lighting based on normals for Point Cloud. Thanks.
Note: I looked at this Stackoveflow question but it only deals with changing the alpha/transparency of points and not lighting.
Adding lighting to a custom material is a very complex process. Especially since you could use Phong, Lambert, or Physical lighting methods, and there's a lot of calculations that need to pass from the vertex to the fragment shader. For instance, this segment of shader code is just a small part of what you'd need.
Instead of trying to re-create lighting from scratch, I recommend you create a PlaneGeometry with the material you'd like (Phong, Lambert, Physical, etc...) and use an InstancedMesh to create thousands of instances, just like in this example.
Based on that example, the pseudo-code of how you could achieve a similar effect is something like this:
const count = 100000;
const geometry = new PlaneGeometry();
const material = new THREE.MeshPhongMaterial();
mesh = new THREE.InstancedMesh( geometry, material, count );
mesh.instanceMatrix.setUsage( THREE.DynamicDrawUsage ); // will be updated every frame
scene.add( mesh );
const dummy = new THREE.Object3D();
update() {
// Sets the rotation so it's always perpendicular to camera
dummy.lookAt(camera);
// Updates positions of each plane
for (let i = 0; i < count; i++){
dummy.position.set( x, y, z );
dummy.updateMatrix();
mesh.setMatrixAt( i ++, dummy.matrix );
}
}
The for() loop would be the most expensive part of each frame, so if you need to update it on each frame, you might want to calculate this in the vertex shader, but that's another question altogether.

Add clipping to THREE.ShaderMaterial

I'm trying to create a shader that takes into account the clipping planes I'm defining in the scene. These clipping planes work fine for all of the 'vanilla' materials I'm using: THREE.MeshLambertMaterial, THREE.MeshPhongMaterial, and THREE.MeshPhysicalMaterial, but THREE.ShaderMaterial is missing this implementation. This is an example of what I mean: https://jsfiddle.net/fraguada/27LrLsv5/
In this example there are two cubes, one with a THREE.MeshStandardMaterial and one with a material defined by THREE.ShaderMaterial. The cube with a THREE.MeshStandardMaterial clips ok. The cube with THREE.ShaderMaterial does not clip.
(I'm not typically defining the vertex/fragment shader in script tags as I show in the jsfiddle, instead I'm defining them in a similar manner to this: https://github.com/mrdoob/three.js/blob/dev/examples/js/shaders/BasicShader.js.)
So, a few questions:
Should THREE.ShaderMaterial include Clipping Planes out of the box? (there is a clipping property, but not sure what it enables)
If not, how could I modify this shader to include the necessary params and shader chunks to enable clipping?
Actually, clipping is done inside the Three.js shaders.
To make it work, you have to handle it inside your shader, by adding those 4 "shader chunks" :
clipping_planes_pars_vertex.glsl at the top of your vertex shader ;
clipping_planes_vertex.glsl inside the main() of your vertex shader ;
clipping_planes_pars_fragment.glsl at the top of your fragment shader ;
clipping_planes_fragment.glsl inside the main() of your fragment shader.
You can access those chunks by simply adding #include <(chunk name)> to your shaders.
Then, set material.clipping = true; and it should work.
Check this fiddle.
Note
To make your shader work, I also added begin_vertex.glsl and project_vertex.glsl.
I checked the current phong shader implementation to understand where to put those chunks.
Note 2
This code should work with a shader implemented with an array of strings but note that you can also reference shader chunks with THREE.ShaderChunk[(chunk name)].
This should be more suitable in your case.

Manual selection lod of mipmaps in a fragment shader using three.js

I'm writing a physically based shader using glsl es in three.js. For the addition of specular global illumination I use a cubemap dds texture with mipmap chain inside (precalculate with CubeMapGen as it's explained here). I need to access this texture in fragment shader and I would like to select manually the index of mipmap. The correct function for doing this is
vec4 textureCubeLod(samplerCube sampler, vec3 coord, float lod)
but it's available only in vertex shader. In my fragment shader I'm using the similar function
vec4 textureCube(samplerCube sampler, vec3 coord, float bias)
but it doesn't work well, because the bias parameter is just added to the automatically calculated level of detail. So, when I zoom in or zoom out on the scene the LOD of mipmap change, but for my shader it must be the same (it must depends only on the rough parameter, as explained in the link above).
I would like to select manually the level of mipmap in fragment shader only depends on the roughness of the material (for example using the formula mipMapIndex = roughness*numMipMap), so it must be costant with the distance and no automatically changed when zooming. How can I solve this?
It wont work with webGL atm, because there is no support for this feature. You can experiment with textureLOD extensions though with recent builds of chrome canary, but it still needs some tweaking. Go about flags and look for this:
Enable WebGL Draft Extensions
WebGL textureCube bias causing seams

Easy way to use light in fragment shader

I have a Collada object which i load per Three.js into my scene.
Now I want to change some vertex positions of the model in the
vertex shader, which is no problem.
But with this I have to skip the exported collada material and take a
ShaderMaterial.
The problem is now that I have to calculate the complete lighting of
the scene in my fragment shader.
Before, with the collada material, the complete lighting was calculated by the framework by using a directional light and a hemisphere light.
So my question is, if there is a solution where I can leave the fragmentShader untouched and all the colors are calculated as if I would use no ShaderMaterial.
I tried to use THREE.ShaderLib and pass only the fragmentShader of the phong shader
and my own vertexShader. But this gave only errors that not the same varyings are defined in both shaders.
Unfortunately, you are correct. You have to create your own ShaderMaterial, and it will be tedious to incorporate scene lighting.
If your scene lighting is not that important, you can hack in some ambient light and a single light at the camera location in your fragment shader.
If your scene lighting is important, then you need to set the ShaderMaterial parameter lights: true, and you will have access to the scene light uniforms in your vertex and fragment shaders.
three.js r.63

Resources