Phong ShaderMaterial parameters / setting Defines - three.js

I'm currently customizing the base phong material using THREE.ShaderMaterial and rebuilding the material with most of THREE js's base chunks and a customized fragment shader chunk. The problem I'm having is with the #define's in many part of three.js and trying to find the proper way to set them.
In the actual program it goes like this
// Clone the uniforms
var uniforms = THREE.UniformsUtils.clone(shader['uniforms']);
// Set uniform values
uniforms["map"].value = texture;
uniforms["diffuse"].value = new THREE.Color( 0xff0000 );
uniforms["envMap"].value = envMapt;
uniforms["reflectivity"].value = 0.7;
// Create material using shader
var material = new THREE.ShaderMaterial( {
vertexShader: shader['vertexShader'],
fragmentShader: shader['fragmentShader'],
uniforms: uniforms,
lights: true,
//map: true, // These don't seem to do anything
//envMap: true // These don't seem to do anything
} );
With a custom shader built like this
fragmentShader: [
"#define USE_MAP",
//"#define USE_ENVMAP",
"uniform vec3 diffuse;",
"uniform float opacity;",
.......
"void main() {",
THREE.ShaderChunk[ "alphatest_fragment" ],
THREE.ShaderChunk[ "specularmap_fragment" ],
......
// NDJ - Using custom frag shader
//THREE.ShaderChunk[ "lights_phong_fragment" ],
CustomShaderChunk[ "lights_phong_fragment" ],
......
THREE.ShaderChunk[ "fog_fragment" ],
"}"
].join("\n")
By manually adding the required #defines at the start of the shaders I can get it to do what I want. However this doesn't seem like the proper way to set it and it isn't very flexible.
Something similar to this, But I only need the base definitions. I've tried looking through the api and examples to find how to set these but can't seem to get it to work.

Just do it exactly as you described. Here is a sample:
ph = new THREE.MeshPhongMaterial( { ambient: 0x000000,
color: 0x0020ff,
specular: 0x2040ff,
shininess: 30,
map: theMap,
side: THREE.DoubleSide } );
ph.defines = {waldo_waldo_three: '(dx+3)', wonke: 7};
if you're being prudent, be careful about overwriting any exsiting 'defines' object

Related

standardMaterial using shaderMaterial. USE_MAP suddenly stopped working, "maptexelToLinear; no matching overloaded function found"

I am using the latest three.js build from github, it was updated the night before.
This code worked a few days back, but without changing the code this stopped working yesterday. It gives the error message maptexelToLinear; no matching overloaded function found on line 6 in the map_fragment shaderChunk:
vec4 texelColor = texture2D( map, vUv );
texelColor = mapTexelToLinear( texelColor ); //here
Did something change? Is this still the correct way of creating a standard material from a shadermaterial? With the defines, extensions and map uniform?
https://jsfiddle.net/EthanHermsey/c4sea1rg/119/
let texture = new THREE.TextureLoader().load(
document.getElementById( 'blockDiff' ).src
);
// this works fine
/* let mat = new THREE.MeshStandardMaterial( {
map: texture
} ) */
// this does not
let mat = new THREE.ShaderMaterial( {
//custom shaders
// vertexShader: document.getElementById( 'blockVertexShader' ).textContent,
// fragmentShader: document.getElementById( 'blockFragmentShader' ).textContent,
//The standard shaders do not even work :/
vertexShader: THREE.ShaderLib[ 'standard' ].vertexShader,
fragmentShader: THREE.ShaderLib[ 'standard' ].fragmentShader,
uniforms: THREE.UniformsUtils.merge( [
THREE.ShaderLib[ 'standard' ].uniforms,
{
blockScale: { value: new THREE.Vector3() } // used in custom shaders
}
] ),
defines: {
"STANDARD": '',
"USE_UV": '',
"USE_MAP": ''
},
lights: true
} );
mat.uniforms.map.value = texture;
mat.extensions.derivatives = true;
mat.uniformsNeedUpdate = true;
There was actually an error in previous three.js versions that injected maptexelToLinear() with a wrong implementation into shader code. This problems was fixed with r118. However, it requires from user code that your custom shader material has a property called map.
Updated code: https://jsfiddle.net/og8Lmp6e/
In this way, it's also not necessary to set custom defines like USE_MAP or USE_UV anymore. That happens automatically. And of course the implementation of maptexelToLinear() now respects the encoding of your texture.
BTW: It's actually best to modify built-in materials with Material.onBeforeCompile().

Three.js ShaderMaterial artifacts

I tried a very simple test using Three.js ShaderMaterial.
I load a 2048x2048 jpg image as a texture for my height map and apply it to deform a PlaneBufferGeometry in the vertex shader.
I also apply the same texture for the diffuse color in the fragment shader.
Globally it works but I see some big artifacts as shown in this screenshot
The artifact always appears along a line parallel to the X axis and passing through the camera.
I have the problem on all three.js version I tried (r105, r114)
The code is yet very simple, anyone know what am I doing wrong ?
Javascript
var textureLoader = new THREE.TextureLoader();
var testTextureBump = textureLoader.load( './front_b.jpg' );
var testGeometry = new THREE.PlaneBufferGeometry(3000, 3000, 500, 500);
var testUniforms = {
uTextureBump: { value: testTextureBump }
};
var testMaterial = new THREE.ShaderMaterial({
uniforms: testUniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.FrontSide,
blending: THREE.NormalBlending,
depthWrite: false,
wireframe: false,
transparent: true
});
var testMesh = new THREE.Mesh( testGeometry, testMaterial );
scene.add( testMesh );
Vertex shader
uniform sampler2D uTextureBump;
varying vec2 vUv;
void main() {
vUv = uv;
vec4 diffuseTexture = texture2D(uTextureBump, uv);
vec3 positionHeight = position.xyz;
positionHeight.z += diffuseTexture.r * 20.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(positionHeight, 1.0);
}
Fragment shader
precision highp float;
precision highp int;
uniform sampler2D uTextureBump;
varying vec2 vUv;
void main (void) {
vec4 texture = texture2D(uTextureBump, vUv);
gl_FragColor = vec4( texture.rgb, 1.0 );
}
You can see the problem in this demo
Move your mouse on the left or right and you'll see the artifacts.
You can fly around as I use the standard THREE.FlyControl service.
The corresponding project file can be download here

THREE.ShaderMaterial depth information lost

In my scene I have two overlapping / crossing plane meshes. The first one uses a MeshBasicMaterial and the second one a custom ShaderMaterial (simple cut-out fragment shader). It seems as if the mesh with the ShaderMaterial doesn't have any depth information as the other plane is always rendered on top of it.
How can I add the plane mesh with the ShaderMaterial to the scene so the collisions and overlapping with other meshes is shown correctly? Do I have to do this in the fragment shader or is it something I have to set up in the material?
Edit: I've made two different variants: A and B. A: works as it should, both Plane Meshes have depth information and use the MeshBasicMaterial:
var movieMaterial = new THREE.MeshBasicMaterial( { map: videoTexture, overdraw: true, side:THREE.DoubleSide } );
Screenshot of Variant A, two crossing Plane Meshes using MeshBasicMaterial
Variant B uses a custom ShaderMaterial on one Plane Mesh:
var movieMaterial = new THREE.ShaderMaterial({
uniforms: {
texture: { type: "t", value: videoTexture }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
"}",
].join("\n"),
fragmentShader: [
"varying vec2 vUv;",
"uniform sampler2D texture;",
"void main() {",
"gl_FragColor = texture2D(texture, vUv);",
"if (gl_FragColor.r + gl_FragColor.b + gl_FragColor.g > 1.5) discard;",
"}",
].join("\n"),
side:THREE.DoubleSide
});
Screenshot of Variant B, now one Plane Mesh is using a custom ShaderMaterial
And now the depth information is lost. The code I posted is the only difference here.
Thanks in advance
This is an old question but I've recently come across this problem. The issue was with the usage of the logarithmic depth buffer. The shaders passed to the ShaderMaterial do not work with it out of the box. Option one is to disable the logarithmic depth buffer. Option two is to append a few pieces of glsl code to your shaders provided by three, something like this:
import { ShaderChunk } from 'three';
const VertexShader = ShaderChunk.common + '\n' + ShaderChunk.logdepthbuf_pars_vertex + `
...
void main() {
...
` + ShaderChunk.logdepthbuf_vertex + `
}
`;
const FragmentShader = ShaderChunk.logdepthbuf_pars_fragment + `
...
void main() {
...
` + ShaderChunk.logdepthbuf_fragment + `
}
`;

Custom ShaderMaterial map issue

I am trying to create a custom ShaderMaterial reusing the ShaderChunks but seem to have a problem when setting the map parameter.
In my custom shader, when I load a texture and set the materials map and uniforms.map.value value to the texture it does not get the values from the texture in my shader, though it does seem to set the USE_MAP definition.
HERE is a fiddle
In the shader, it turns green if USE_MAP is defined, but it should also get the map value.
Am I missing something simple?
Below are the important bits :)
Set up the shader
var shaderUniforms = THREE.UniformsUtils.merge([THREE.UniformsLib[ "common" ]]);
var shaderVertex = [
//THREE.ShaderChunk[ "common" ],
THREE.ShaderChunk[ "uv_pars_vertex" ],
"void main() {",
THREE.ShaderChunk[ "begin_vertex" ],
THREE.ShaderChunk[ "project_vertex" ],
"}",
].join("\n");
var shaderFragment = [
THREE.ShaderChunk[ "common" ],
THREE.ShaderChunk[ "uv_pars_fragment" ],
THREE.ShaderChunk[ "map_pars_fragment" ],
"void main() {",
"vec4 diffuseColor = vec4(1.0, 0.0, 0.0, 1.0);",
"#ifdef USE_MAP",
' diffuseColor = vec4(0.0, 1.0, 0.0, 1.0);',
'#endif',
THREE.ShaderChunk[ "map_fragment" ],
"gl_FragColor = diffuseColor;",
"}",
].join("\n");
Create the material
material = new THREE.ShaderMaterial({
uniforms: shaderUniforms,
vertexShader: shaderVertex,
fragmentShader: shaderFragment
});
Add the texture
function loadTexture() {
var loader = new THREE.TextureLoader();
loader.load(dataurl, function(texture){
console.log('texture loaded ok');
// What to do here to add texture (map) and update ShaderMaterial?
// Do I have to set needsUpdate on the texture?
texture.needsUpdate = true;
// Do I have to set needsUpdate on the material?
material.needsUpdate = true;
// Seems I have to set both the uniform map value and the material map value...
material.uniforms.map.value = texture;
material.map = texture;
// But still does not show the texture, though USE_MAP does seem to get defined in the shader (color changes to green).
// Hmmmm
});
}

Three.js: Wireframe renderd behind ShaderMaterial

Because I needed transparency for every face on its own I switched from MeshBasicMaterial to ShaderMaterial.
I draw my geometry twice:
First my filled trianges
and then a wireframe to get a border for every triangle.
Is there a better way to archive this?
With MeshBasicMaterial it looks fine:
But if I switch to ShaderMaterial: (Opacity is reduced to .3 so that you can see the wireframe)
Is there a way to tell webgl which shader "comes first"?
My MeshBasicMaterial:
var material = new THREE.MeshBasicMaterial({
color: new THREE.Color(0xa5a5a5),
side: THREE.DoubleSide,
transparent: true,
opacity: .99
});
and
var materialLines = new THREE.MeshBasicMaterial({
color: new THREE.Color(0x0),
side: THREE.DoubleSide,
wireframe: true
});
My ShaderMaterial:
var attributes = {
customColor: { type: 'c', value: [] },
customOpacity: { type: 'f', value: []}
};
var shaderMaterial = new THREE.ShaderMaterial({
attributes: attributes,
vertexShader: document.getElementById('vertexshader').textContent,
fragmentShader: document.getElementById('fragmentshader').textContent,
blending: THREE.NormalBlending,
depthTest: false,
transparent: true,
side: THREE.DoubleSide
});
shaderMaterial.linewidth = 5;
and
var uniforms = {
color: { type: "c", value: new THREE.Color(0x0) }
};
var ShaderMaterialLines = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexshaderline').textContent,
fragmentShader: document.getElementById('fragmentshaderline').textContent,
depthTest: false,
side: THREE.DoubleSide,
wireframe: true
});
with my shaders:
<script type="x-shader/x-vertex" id="vertexshader">
attribute vec3 customColor;
attribute float customOpacity;
varying vec3 vColor;
varying float vOpacity;
void main() {
vColor = customColor;
vOpacity = customOpacity;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
varying vec3 vColor;
varying float vOpacity;
void main() {
gl_FragColor = vec4( vColor, vOpacity);
}
</script>
<script type="x-shader/x-vertex" id="vertexshaderline">
uniform vec3 color;
varying vec3 vColor;
void main() {
vColor = color;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentshaderline">
varying vec3 vColor;
void main() {
gl_FragColor = vec4( vColor, 1.0);
}
</script>
Edit 1:
What exactly are you trying to achieve?
I want to draw a 3D object that consist of triangles.
I want to have the possibility to control transparency and color for every triangle.
What are the requirements?
The user should see every triangle edge / a border around every triangle.
Each triangle surfaces can have a different color (based on the color of the three corners) and alpha / transpareny value.
The user can set every triangle to invisible (opacity = 0.0), visible (opacity = 1.0) or something between.(Only the triangle surface not the border)
What is your question?
What is the best way to draw triangles with a border in black or whatever color.
What is the best way to get transparency for every triangle( but keep the border).
EDIT - answer updated. WireframeHelper has been deprecated.
You want your mesh to have both a transparent material and a wireframe.
To render a border around each triangle, use WireframeGeometry, and make sure your mesh material has transparent = true.
Transparent objects are rendered last, so the entire wireframe will show.
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
// wireframe
var geometry2 = new THREE.WireframeGeometry( geometry ); // or EdgesGeometry
var material2 = new THREE.LineBasicMaterial( { color: 0x000000, transparent: true } );
var wireframe = new THREE.LineSegments( geometry2, material2 );
mesh.add( wireframe );
three.js r.84

Resources