Three.js - Put two materials on the same object - three.js

I was wondering how to put two materials on the same object...
I have a shader material that I use for the colors, but I also want to have a basing lambert shading.
Here is the code :
var material1 = new THREE.ShaderMaterial( {
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
var material2 = new THREE.MeshLambertMaterial({
color: 0xffffff,
});
var materials = [material1, material2];
var group = new THREE.Object3D();
var plane = new THREE.Mesh(geometry, materials);
Can you help me ?

Here is a fiddle that shows how to incorporate the lambert chuck into your own shader.
Is this what you wanted?
It calculates in the vertex shader the same as the THREE.MeshLambertMaterial.
var myShader = {
uniforms : THREE.UniformsUtils.merge([
THREE.UniformsLib['lights'],
{
color : {type:"c", value:new THREE.Color(0x0000ff)}
}
]),
vertexShader : [
THREE.ShaderChunk[ "common" ],
'varying vec3 vLightFront;',
THREE.ShaderChunk[ "lights_lambert_pars_vertex" ],
'void main() {',
' vec3 transformedNormal = normalMatrix * normal;',
' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
THREE.ShaderChunk[ "lights_lambert_vertex" ],
' gl_Position = projectionMatrix * mvPosition;',
'}'
].join('\n'),
fragmentShader : [
'uniform vec3 color;',
'varying vec3 vLightFront;',
'void main() {',
' vec3 outgoingLight = color.rgb + vLightFront;',
' gl_FragColor = vec4(outgoingLight, 1.0);',
'}',
].join('\n'),
};
material = new THREE.ShaderMaterial({
uniforms : myShader.uniforms,
vertexShader : myShader.vertexShader,
fragmentShader : myShader.fragmentShader,
lights: true
});

Related

ThreeJS: First shader attempt - blending two textures

I'm trying to blend two textures without success. I just get a black texture.
I'm not sure if it is the shader code or the way I'm implementing it using ThreeJS.
I've tried two different shader methods, but I just get a black texture on both of them:
Test1:
var pitchMaterialParams = {
uniforms: THREE.UniformsUtils.merge([{
texture1: { type: "t", value: texture1 },
texture2: { type: "t", value: texture2 },
}]),
vertexShader:
`
precision highp float;
precision highp int;
//uniform mat4 modelViewMatrix;
//uniform mat4 projectionMatrix;
//attribute vec3 position;
//attribute vec2 uv;
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`,
fragmentShader:
`
precision mediump float;
uniform sampler2D texture1;
uniform sampler2D texture2;
varying vec2 vUv;
void main() {
vec4 t1 = texture2D( texture1, vUv );
vec4 t2 = texture2D( texture2, vUv );
//gl_FragColor = mix( t1, t2, t2.a );
gl_FragColor = vec4(mix(t1.rgb, t2.rgb, t2.a), 1.0);
}
`};
https://jsfiddle.net/Eketol/doLgv9cw/
Test 2:
var pitchMaterialParams = {
uniforms: THREE.UniformsUtils.merge([{
fade: { type: "f", value: 0 },
u_texture1: { type: "t", value: texture1 },
u_texture2: { type: "t", value: texture2 }
}]),
vertexShader:
`
precision highp float;
precision highp int;
varying vec2 v_uv;
varying float v_textureIndex;
void main() {
v_textureIndex = step(0.5, uv.x) + step(0.5, uv.y) * 2.0;
v_uv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader:
`
precision mediump float;
varying vec2 v_uv;
varying float v_textureIndex;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
void main() {
gl_FragColor = mix( texture2D(u_texture1, v_uv), texture2D(u_texture2, v_uv), step(0.5, v_textureIndex) );
}
`};
https://jsfiddle.net/Eketol/qm435wL7/
Basically I just want to blend two images using an overlay/multiply method, where the top one is white with some transparent areas.
Any help will be appreciated.
You have run into the following issue:
https://github.com/mrdoob/three.js/issues/8016
Just assign your textures after you have merge the uniforms.
https://jsfiddle.net/r4nmf2wt/1/
three.js R112
This part:
uniforms: THREE.UniformsUtils.merge([{
texture1: { type: "t", value: texture1 },
texture2: { type: "t", value: texture2 },
}])
better to keep it simpler:
uniforms: {
texture1: { value: texture1 },
texture2: { value: texture2 }
}
And shaders could be like that:
vertexShader:
`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader:
`
varying vec2 vUv;
uniform sampler2D u_texture1;
uniform sampler2D u_texture2;
void main() {
vec4 col1 = texture2D(u_texture1, vUv);
vec4 col2 = texture2D(u_texture2, vUv);
col2 = col2.a > 0.5 ? col2 : vec4(0, 0, 0, 1);
gl_FragColor = mix( col1, col2, 0.25 );
}
`
(Posted solution on behalf of the question author, to move it to the answer space).
I have a solution thanks to #Mugen87 and #prisoner849:
var camera, scene, renderer, object;
var texture1URL = "https://cdn.rawgit.com/mrdoob/three.js/45418089bd5633e856384a8c0beefced87143334/examples/textures/terrain/grasslight-big.jpg";
var texture2URL = "https://cdn.rawgit.com/mrdoob/three.js/45418089bd5633e856384a8c0beefced87143334/examples/textures/patterns/circuit_pattern.png";
var status = document.getElementById("status");
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xcccccc );
var ambientLight = new THREE.AmbientLight( 0xffffff, 1 );
scene.add( ambientLight );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 500 );
camera.enableDamping = false;
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = -15;
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.gammaFactor = 2.14;
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableDamping = true;
//var gridHelper = new THREE.GridHelper(10, 20, 0x333333, 0x333333);
//scene.add( gridHelper );
}
// -------------------- Object -------------------- //
function initObject() {
var texture1 = new THREE.TextureLoader().load( texture1URL );
var texture2 = new THREE.TextureLoader().load( texture2URL );
var pitchMaterialParams = {
uniforms: THREE.UniformsUtils.merge([{
texture1: null,
texture2: null,
}]),
vertexShader:
`
precision highp float;
precision highp int;
//uniform mat4 modelViewMatrix;
//uniform mat4 projectionMatrix;
//attribute vec3 position;
//attribute vec2 uv;
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader:
`
precision mediump float;
uniform sampler2D texture1;
uniform sampler2D texture2;
varying vec2 vUv;
void main() {
//vec4 t1 = texture2D( texture1, vUv );
//vec4 t2 = texture2D( texture2, vUv );
//gl_FragColor = mix( t1, t2, t2.a );
vec4 col1 = texture2D(texture1, vUv);
vec4 col2 = texture2D(texture2, vUv);
col2 = col2.a > 0.5 ? col2 : vec4(0, 0, 0, 1);
gl_FragColor = mix( col1, col2, 0.25 );
}
`
};
// Dummy box for reference.
//scene.add( new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshStandardMaterial({color:0xff0000})) );
var material = new THREE.ShaderMaterial(pitchMaterialParams);
material.uniforms.texture1.value = texture1;
material.uniforms.texture2.value = texture2;
material.side = THREE.DoubleSide;
var geometry = new THREE.PlaneGeometry(10, 10);
var object = new THREE.Mesh(geometry, material);
scene.add( object );
}
// -------------------- Events -------------------- //
function initEvents() {
window.addEventListener("resize", onWindowResize);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
init();
initObject();
initEvents();
animate();
https://jsfiddle.net/Eketol/ynsLrwg6/

How to fit noise shader into my plane geometry

The following link is my pen:
https://codepen.io/johnhckuo/pen/RxrXxX
And here is my code in fragment shader:
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
vec3 worldtoEye = eye - worldPosition;
vec3 eyeDirection = normalize(worldtoEye);
vec3 pos = vec3(st.x*5.0, st.y*5.0, u_time*0.5);
color = vec3(fbm(pos) + 1.0);
//color = vec3(noise(pos)*0.5 + 1.0);
vec3 sunLight = vec3(1., 1., 1.);
color *= (diffuseLight(sunLight) + specularLight(eyeDirection));
vec3 oceanBlue = vec3(0.109, 0.419, 0.627);
gl_FragColor = vec4(oceanBlue * color, 1.0);
}
Here is the code of plane geometry:
var plane_geometry = new THREE.PlaneBufferGeometry( 2000, 2000, 32 );
var customMaterial = new THREE.ShaderMaterial(
{
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
}
);
customMaterial.side = THREE.DoubleSide;
var surface = new THREE.Mesh( plane_geometry, customMaterial );
surface.position.set(0,0,0);
scene.add( surface );
I've created a shader and tried to apply it onto my plane.
But whenever I zoom in/out, the shader since to be fixed to the screen and not zooming in/out correspondingly.
Any suggestions is appreciated !
Change this line:
vec2 st = gl_FragCoord.xy/u_resolution.xy;
to
vec2 st = uVu.xy* 2.0;
and fiddle with 2.0 (the resolution)
It's also easy to test out vert + frag shaders on ShaderFrog, as in: https://shaderfrog.com/app/view/1997

replacing mirror.js shaderLib['mirror'] with built in material

I am trying to reuse the existing basic, lambert, and phong material so I can have bumps, normal maps, ..etc on my mirror reflections. I am using the latest revision of three.js which is 87.
I have tried using the onBeforeCompile function to append and replace part of the MeshBasicMaterial vertex and fragment code but my mirror is not showing.
Can someone please help. Thank you.
Here is what I have did in the mirror.js
/* commented out the existing code that use the shaderLib mirror shader
var mirrorShader = THREE.ShaderLib[ "mirror" ];
var mirrorUniforms = THREE.UniformsUtils.clone( mirrorShader.uniforms );
//console.log('opacity:', this.mirror_opacity)
mirrorUniforms['opacity'].value = this.mirror_opacity;
this.material = new THREE.ShaderMaterial( {
fragmentShader: mirrorShader.fragmentShader,
vertexShader: mirrorShader.vertexShader,
uniforms: mirrorUniforms,
transparent: true
} );
this.material.uniforms.mirrorSampler.value = this.renderTarget.texture;
this.material.uniforms.mirrorColor.value = mirrorColor;
this.material.uniforms.textureMatrix.value = this.textureMatrix;
*/
/* Here is the material I am replacing with */
var textureLoader = new THREE.TextureLoader();
var texture = textureLoader.load("images/test.jpg");
this.material = new THREE.MeshBasicMaterial({ map: texture});
var mirror_texture = this.renderTarget.texture;
this.material.onBeforeCompile = function ( shader ) {
let mirrormap_pars_vertex = [
'uniform mat4 textureMatrix;',
'varying vec2 texCoord;',
'varying vec4 mirrorCoord;\n',
] .join('\n');
shader.vertexShader = mirrormap_pars_vertex + shader.vertexShader;
shader.vertextShader = shader.vertexShader.replace(
'#include <begin_vertex>',
[
'vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
'vec4 worldPosition = modelMatrix * vec4( position, 1.0 );',
'mirrorCoord = textureMatrix * worldPosition;',
'texCoord = uv;',
'gl_Position = projectionMatrix * mvPosition;\n',
].join( '\n' )
);
let mirrormap_pars_fragment = [
'uniform vec3 mirrorColor;',
'uniform sampler2D mirrorSampler;',
'uniform sampler2D maskSampler;',
'varying vec4 mirrorCoord;',
'varying vec2 texCoord;\n',
].join('\n');
shader.fragmentShader = mirrormap_pars_fragment + shader.fragmentShader;
shader.fragmentShader = shader.fragmentShader.replace(
'#include <map_fragment>',
[
'#ifdef USE_MAP\n',
'vec4 mask = texture2D(maskSampler, texCoord);',
'vec4 color = texture2DProj(mirrorSampler, mirrorCoord);',
//'vec4 texelColor = texture2D( map, vUv );',
//'color = vec4(blendOverlay(mirrorColor.r, color.r), blendOverlay(mirrorColor.g, color.g), blendOverlay(mirrorColor.b, color.b), opacity);',
//'color.a *= texture2D(maskSampler, texCoord).g;'
'diffuseColor *= color;',
'#endif\n',
].join( '\n' )
);
shader.uniforms.mirrorSampler = {type : "t", value : mirror_texture};
shader.uniforms.textureMatrix = { type: "m4", value: new THREE.Matrix4() };
shader.uniforms.maskSampler = {type: "t", value: null}
shader.uniforms.mirrorColor = { type: "c", value: new THREE.Color(0x7F7F7F) };
console.log("shader", shader )

Three.js Shader material how get (map) texture?

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.

Issue with threejs and shaders showing an sphere glow

I am working on a 3d visualization. I need to show a world with its atmosphere and I based on an example that was with threejs r40 but mine is r71.
When I try to add the atmosphere (kind of a glow to the sphere that represents the world) I am getting the following error:
three.min.js:445 Uncaught TypeError: c.addEventListener is not a function
My code is the following:
scene.add(createAtmosphere());
function createAtmosphere() {
shader = Shaders['atmosphere'];
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
mesh = new THREE.Mesh(new THREE.Sphere(200, 40, 30),
new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader
})
);
mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.1;
mesh.flipSided = true;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
return mesh;
}
and this is the shader
var Shaders = {
'atmosphere' : {
uniforms: {},
vertexShader: [
'varying vec3 vNormal;',
'void main() {',
'vNormal = normalize( normalMatrix * normal );',
'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
'}'
].join('\n'),
fragmentShader: [
'varying vec3 vNormal;',
'void main() {',
'float intensity = pow( 0.8 - dot( vNormal, vec3( 0, 0, 1.0 ) ), 12.0 );',
'gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 ) * intensity;',
'}'
].join('\n')
}
};
I solved another shader issue but I cant find what is wrong in this one (I am not familiar with shaders and it driving me crazy).
Thanks in advance
You have to create your mesh sending a SphereGeometry, not a Sphere.
http://threejs.org/docs/#Reference/Objects/Mesh
http://threejs.org/docs/#Reference/Extras.Geometries/SphereGeometry
scene.add(createAtmosphere());
function createAtmosphere() {
shader = Shaders['atmosphere'];
uniforms = THREE.UniformsUtils.clone(shader.uniforms);
mesh = new THREE.Mesh(new THREE.SphereGeometry(200, 40, 30),
new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: shader.vertexShader,
fragmentShader: shader.fragmentShader
})
);
mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.1;
mesh.flipSided = true;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
return mesh;
}

Resources