I have a model loaded using THREE.OBJMTLLoader.
var loader = new THREE.OBJMTLLoader();
loader.addEventListener('load', function(event) {
var mesh = event.content;
scene.add(mesh);
});
loader.load('model/machine.obj', 'model/machine.mtl');
I need to apply a vertex and fragment shader to this model. How to do this?
In addition to Gero3's answer, this would ensure that all the meshes in the content will get the right material:
var material = new THREE.ShaderMaterial( {
uniforms: shader.uniforms,
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader
} );
var object = event.content;
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = material;
}
} );
scene.add( object );
You need a shadermaterial for adding a vertex and fragment shader.
var material = new THREE.ShaderMaterial( {
fragmentShader: shader.fragmentShader,
vertexShader: shader.vertexShader,
uniforms: shader.uniforms
} ),
Related
I try several times to implement the edges in a model loader with the OBJLoader but can't do it.
Mloader = new THREE.MTLLoader();
Mloader.setPath( dir );
Mloader.load( mtl_dir, function ( materials ) {
materials.preload();
OLoader = new THREE.OBJLoader();
OLoader.setMaterials( materials );
OLoader.setPath( dir );
OLoader.load( name_file, function ( object ) {
object.scale.set( scale, scale, scale );
scene.add( object );
var edges = new THREE.EdgesGeometry( object, 11 );
var line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( {color: 0x111111 } ) );
line.scale.set( scale, scale, scale );
scene.add( line )
} );
} );
The model load fine, but the edges don't.
When the model is loader with the STLloader the edges of the geometry render fine, but i need to do it with .obs files.
var loader = new THREE.STLLoader();
loader.load(dir, function (geometry) {
material = new THREE.MeshPhongMaterial({
color: 0xAAAAAA,
specular: 0x111111,
shininess: 200
});
var edges = new THREE.EdgesGeometry(geometry, 11);
var line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({color: 0x111111}));
line.scale.set(scale, scale, scale);
scene.add(line)
});
STL vs OBJ Loader
Thanks, yes it a group. The code with the solution:
Mloader = new THREE.MTLLoader();
Mloader.setPath( dir );
Mloader.load( mtl_dir, function ( materials ) {
materials.preload();
OLoader = new THREE.OBJLoader();
OLoader.setMaterials( materials );
OLoader.setPath( dir );
OLoader.load( name_file, function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.castShadow = true;
edges = new THREE.EdgesGeometry( child.geometry,11);
line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( {
color: 0x111111
} ) );
line.scale.set( scale, scale, scale );
line.position.set( 0, 0.7, 0 );
scene.add( line );
}
} );
object.scale.set( scale, scale, scale );
object.position.set( 0, 0.7, 0 );
scene.add( object );
} );
} );
I had to re-write my question because I was asking the wrong thing before.
I meant to ask, how do you update a uniform in three.js?
This seems to work:
yourMesh.material.uniforms.yourUniform.value = whatever;
There are 2 Ways:
You can update uniform in Shader Material itself
OR
you can use mesh.material to access the ShaderMaterialand then update the uniform
Example of both cases:
var delta = 0
var customUniforms = {
delta: { value: 0 },
u_time: { value: Date.now() }
};
// shader material with custom Uniform
shaderMaterial = new THREE.ShaderMaterial({
uniforms: customUniforms,
vertexShader: document.getElementById("vertexShader2").textContent,
fragmentShader: document.getElementById("fragmentShader2").textContent
});
// test Mesh object for shader
var geometry = new THREE.BoxBufferGeometry(10, 10, 10, 10, 10, 10);
shaderMesh = new THREE.Mesh(geometry, shaderMaterial);
this.scene.add(shaderMesh);
In animate loop
animate = () => {
delta += 0.1;
// Update uniform in Shader Material
shaderMaterial.uniforms.delta.value = 0.5 + Math.sin(delta) * 0.0005;
// Update uniform from Mesh itself
shaderMesh.material.uniforms.u_time.value = delta;
}
https://codesandbox.io/s/autumn-http-e9wk5
Complete Example
<body>
<div id="container"></div>
<script src="js/three.min.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
void main() {
gl_Position = vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 u_resolution;
uniform float u_time;
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
gl_FragColor=vec4(st.x,st.y,0.0,1.0);
}
</script>
<script>
var container;
var camera, scene, renderer;
var uniforms;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.Camera();
camera.position.z = 1;
scene = new THREE.Scene();
var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
uniforms = {
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new THREE.Vector2() },
u_mouse: { type: "v2", value: new THREE.Vector2() }
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
container.appendChild( renderer.domElement );
onWindowResize();
window.addEventListener( 'resize', onWindowResize, false );
document.onmousemove = function(e){
uniforms.u_mouse.value.x = e.pageX
uniforms.u_mouse.value.y = e.pageY
}
}
function onWindowResize( event ) {
renderer.setSize( window.innerWidth, window.innerHeight );
uniforms.u_resolution.value.x = renderer.domElement.width;
uniforms.u_resolution.value.y = renderer.domElement.height;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
uniforms.u_time.value += 0.05;
renderer.render( scene, camera );
}
</script>
</body>
Little example updating the shader uniform.
/* Vertex Shader */
<script type="x-shader/x-fragment" id="myShader">
uniform float myuniform;
uniform sampler2D myTexture;
varying vec2 vUV;
varying vec2 ver;
ver = uv *vec2( myuniform, myuniform); // this will be updated on mouse move
gl_Position = projectionMatrix *modelViewMatrix * vec4(position,1.0);
</script>
/* Set uniform */
var myUniform;
var myTexture = new THREE.ImageUtils.loadTexture( './data/textures/theTexture.jpg' );
_uniforms = {
myUniform: { type: "f", value: myUniform },
myTexture: { type: "t", value: myTexture },
};
customMaterial = new THREE.ShaderMaterial(
{
uniforms: _uniforms,
vertexShader: document.getElementById( 'myShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
wireframe: false,
side: THREE.FrontSide
} );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
function onDocumentMouseMove(ev) {
uniforms.myUniform.value += 0.01; // Updates this new value
uniforms.myUniform.needsUpdate = true;
}
...
This seemed like a good way to do it for my use case:
const mat = new THREE.ShaderMaterial({
uniforms: {
customVec3Uniform: { value: new THREE.Vector3(1,1,1) }
},
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
});
As for updating during runtime (citation):
mat.uniforms.customVec3Uniform.value = new THREE.Vector3(2,2,2);
// mat.needsUpdate = true; // my example works without 'needsUpdate'
For those not using THREE.ShaderMaterial
If for example you are modifying a material beyond just THREE.ShaderMaterial, say THREE.MeshStandardMaterial, via edits to its fragment shader, you will need to do things somewhat differently.
In order to modify this shader's uniforms, WITHOUT requiring recompilation, you will need to store a reference to that shader within the onBeforeCompile callback, then access those uniforms via that stored reference's uniforms.
Typescript class:
First plug into THREE.Material's onBeforeCompile hook transferring the values stored somewhere (in my case a uniforms dictionary created in the constructor) into the shader's uniforms. This is important because shader compilation takes place prior to first usage. Store a reference to the shader in your class for access. Do any of your other work to the fragment shader using these uniforms as you see fit.
private _uniforms: { [uniform: string]: ShaderUniform } = {};
private _shader?:THREE.Shader;
this._material.onBeforeCompile = (shader) => {
let prepend = "";
//transfer any changes occurring prior to compilation,
//and also prepend these shaders to the fragment shader
Object.entries(this._uniforms).forEach(([key, info]) => {
prepend += `uniform ${info.type} ${key};\n`
shader.uniforms[key] = {value: info.value};
});
//prepend these shaders, along with any other work to the
//fragment shader via basic string substitutions
shader.fragmentShader = prepend + shader.fragmentShader;
//store a reference to the shader
this._shader = shader;
}
Get or set the property from the stored uniforms if not compiled yet, otherwise use the shader's own uniform value, which only exists after compilation.
public getUniform(name:string) : any {
return this._shader ? this._shader.uniforms[name].value : this._uniforms[name].value;
}
public setUniform(name:string, value:any) {
if (this._shader) {
return this._shader.uniforms[name].value = value
} else {
this._uniforms[name].value = value;
}
}
How do I attach a texture to JSON object in Three.js?
Someone help me. Please see my inability cord below.
var texture = new THREE.ImageUtils.loadTexture('panel.png');
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 1, 1 );
var loader = new THREE.JSONLoader();
loader.load( 'panel.json', function ( geometry ) {
model = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial( {map: texture} ) );
scene.add( model )
} );
Try below codes, define the material in your json file.
var loader = new THREE.JSONLoader();
loader.load( 'panel.json', function ( geometry, material ) {
model = new THREE.Mesh( geometry, material);
scene.add( model )
}, 'panel.png');
i import the obj file to editor and then export geometry to js file, when i put it on my code, nothing appear with the error in firefox debug:"Type Error: t1 is undefined" .
I change model to the .js file exported by MaxExporter with option "Export UVs", "Export Normals" selected, it's work fine.
Next, i change model to file exported by MaxExporter with only "Export UVs" selected, once again, "Type Error: t1 is undefined", so is this a bug or the problem is just my code, and how to fix it?
This is my code:
var ambient = 0xffffff, diffuse = 0xffffff, specular = 0x000000, shininess = 100;
var shader = THREE.ShaderLib[ "normalmap" ];
var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
uniforms[ "tNormal" ].value = THREE.ImageUtils.loadTexture( "md/normals.jpg" );
uniforms[ "uNormalScale" ].value.set( 1.5, 1.5 );
uniforms[ "tDiffuse" ].value = THREE.ImageUtils.loadTexture( "md/diff.jpg" );
uniforms[ "enableDiffuse" ].value = true;
uniforms[ "uDiffuseColor" ].value.setHex( diffuse );
uniforms[ "uSpecularColor" ].value.setHex( specular );
uniforms[ "uAmbientColor" ].value.setHex( ambient );
uniforms[ "uShininess" ].value = shininess;
var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true };
var material = new THREE.ShaderMaterial( parameters );
loader = new THREE.JSONLoader( true );
loader.load( "md/model.js", function( geometry ) { createScene( geometry, 100, material ) } );
container.appendChild( renderer.domElement );
window.addEventListener('resize', onWindowResize, false);
}
function createScene( geometry, scale, material ) {
geometry.computeTangents();
geometry.computeVertexNormals();
mesh = new THREE.Mesh( geometry, material );
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.position.y = - 0;
mesh.scale.x = mesh.scale.y = mesh.scale.z = scale;
scene.add( mesh );
}
There is a problem with your code.
geometry.computeTangents() requires vertex normals. You need to switch the order.
geometry.computeVertexNormals();
geometry.computeTangents();
That fixes the error.
But, geometry.faces[4].normal is zero. There is something wrong with your model.
three.js.r.63
I am trying to use a custom shader with Three.js. I tried to do it like the many examples, but it doesn't work. My code is:
var vertex = "void main(){vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );gl_Position = projectionMatrix * mvPosition;}";
var fragment = "precision highp float;void main(void){gl_FragColor = vec4(0.0,1.0,0.0,1.0);}";
material = new THREE.ShaderMaterial({
vertexShader: vertex,
fragmentShader: fragment
});
var mesh = new THREE.Mesh(geometry,material);
…and everything is blank. But if I use this material :
material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true });
…everything works perfectly. What's wrong?
I found the problem: I had to use:
renderer = new THREE.WebGLRenderer();
instead of :
renderer = new THREE.CanvasRenderer();