Closed landonsilla closed 11 years ago
I think you'll have to write your custom vertex shader and send some uniforms that define the limits.
This example should be a good reference (take a look at the custom vertex/fragment shader): http://mrdoob.github.com/three.js/examples/webgl_kinect.html
However, you'll have to figure out how to get the pieces of shader code that you currently rely on (for coloring the points).
I think you should be able to figure out the code you rely on by doing
console.log( material.vertexShader, material.fragmentShader )
and pick the pieces of code from there and compose your own shader.
Thank you mrdoob. I think I have some test code running that I can polish up into a final product to get the effect I want for a ParticleSystem
. I will gladly post my solution here once it's done for others to use/borrow. However, half of my application will rely on meshes. How can I do this with meshes considering meshes don't use shaders (I think). Here's a test function that I'm working on:
<a href="#" onclick="setXClippingPlane();return false;">move plane</a>
var xclip=0;
function setXClippingPlane(){
xclip++;
uniforms['xClippingPlane'].value=xclip;//this is used to send to the vertex shader that filters points out of the box
for(var i=0;i<group_model.children.length;i++){//go through all the items in the group_model
for (var j = 0; j<group_model.children[i].geometry.faces.length; j++){//evaluate all the faces in that given geometry
var firstcoordinateinface = group_model.children[i].geometry.faces[j].a;
if(group_model.children[i].geometry.vertices[firstcoordinateinface].position.x > xclip)//is the x val greater than the limit?
group_model.children[i].geometry.faces[j].visible = false;//hide a specific face
else
group_model.children[i].geometry.faces[j].visible = true;
}
}
}
I what it's pretty easy to see what I'm trying to do. Loop through the geometries in my group_model object. Loop through the faces in that geometry. Is the x value of the first vertex in that face greater than some value? If so, hide it, otherwise show it.
I've tried a bunch of stuff here including playing with the "dirty" flags on both the geometries and the faces. The only thing I'm able to do is hide the whole geometry. I'm looking for a way to hide various faces within a given geometry. How do I do this?
For bonus points, I have a harder question. Suppose I have a triangle, and my limit box goes through that triangle. I can adjust my logic above to say "is the centroid inside/outside the limit box" or "are all of the vertices inside/outside the limit box" or any other similar question. In the event of a limit box intersecting a triangle, is it possible to show the portion of the triangle that is inside the box and hide the other part that's outside the box? That would be the best solution here, but I'm not sure that's possible.
For your 'bonus' question, CSG may help. See: http://evanw.github.com/csg.js/ and http://learningthreejs.com/blog/2011/12/10/constructive-solid-geometry-with-csg-js/
@WestLangley thank you for your response. That's a very interesting library. I tried too hook that up and run a test and the browser just locked up on me. My geometries are very complex being in the 100,000s of polygons. I imagine this library is evaluating each of those faces with the basic cube box i'm doing with the boolean logic. This sounds expensive. I did some time testing and found the conversion from three.js format to csg format took a blink of the eye, creating the cube was instant. Then performing the union was very very expensive. I couldn't get through it in my chrome browser.
Any other ideas? How can I create a limit box for meshes in three.js?
@landonsilla Hmm... I'd love to see one of your demos using CSG with, say, a few thousand polys -- even if only to show others exactly what you are trying to achieve. Once you have it working as you envision it, then worrry about the speed.
Everything uses shaders.
For meshes you could use custom ShaderMaterial
based on whichever mesh material you use now plus extra custom attribute which would be face centroid.
Then you would do the same magic like you do for culling particles (all vertices from the face would get the same centroid, so whole face would be on/off, depending on whether centroid would be inside clipping region).
Or maybe you could just deal with world positions in the shader directly, then it should be possible to clip just parts of faces.
Something like this:
// vertex shader
varying vec4 worldPosition;
worldPosition = objectMatrix * vec4( position, 1.0 );
// fragment shader
uniform float xClippingPlaneLo;
uniform float xClippingPlaneHi;
varying vec4 worldPosition;
if ( worldPosition.x < xClippingPlaneLo || worldPosition.x > xClippingPlaneHi ) discard;
I'm pretty new to the 3d/webgl world. A lot of these concepts are unfamiliar to me. I found and read this article by Paul Lewis:
http://www.html5rocks.com/en/tutorials/webgl/shaders/
To any future novice readers, it's a GREAT introduction to what shaders can do and what power they have. The ability to move a million vertices without absolutely killing your frame rate is very impressive.
After reading that and coming back to this thread, @alteredq, your code was pretty close to what I want and works very well. But now, I'm having problems with the material. My meshes are supposed to be textured and I only found one material that supports that MeshFaceMaterial
. On my json obj loader, I had:
var mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial({}) );
But in order to get my shader logic to work, I had to change the type of material I was using from MeshFaceMaterial
to ShaderMaterial
:
var material = new THREE.MeshFaceMaterial({
uniforms: uniformsmesh,
vertexShader: document.getElementById( 'vertexshader-mesh' ).textContent,
fragmentShader: document.getElementById( 'fragmentshader-mesh' ).textContent
});
var mesh = new THREE.Mesh( geometry, material );
So how do I fix this? I guess I don't 100% know what materials are or how they work. I just plugged a few different ones in until I got one that I liked. Can you send vertex/fragment shaders to any material other than ShaderMaterial
?
MeshFaceMaterial
is not "proper" material, it's just dummy pass-through material for using real materials defined in geometry.materials
(so that single mesh can have faces with different materials).
All standard materials (e.g MeshBasicMaterial
, MeshLambertMaterial
, MeshPhongMaterial
) internally use the same machinery as ShaderMaterial
, they are basically just shortcuts for commonly used features.
You can check how they are implemented here:
https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLShaders.js
If you already have something working without texture, it's just few lines to add to get texture to your ShaderMaterial
.
See this example:
https://github.com/mrdoob/three.js/blob/master/examples/webgl_custom_attributes.html
Thank you for your response, @alteredq , however I'm still stuck. I adjusted my code to use ShaderMaterial
for my material:
var material = new THREE.ShaderMaterial({
uniforms: uniformsmesh,
vertexShader: document.getElementById( 'vertexshader-mesh' ).textContent,
fragmentShader: document.getElementById( 'fragmentshader-mesh' ).textContent
});
I read through the shader definition javascript file. And I checked out the demo you sent; that demo doesn't seem to help. It shows how you can use attributes and uniforms to send data to the shader. That's something that I can already do in my application with the use of ShaderMaterial
. All I need to do is to have ShaderMaterial
use the associated jpg's to texture the mesh as defined in the obj/mtl files (that was converted into json format).
One puzzling thing was that I looked in MeshFaceMaterial.js
to see how it works for the phototexturing, and it's just a blank function. Where is the code that performs the phototexturing associated with MeshFaceMaterial
?
In any event, you said it will just take a few lines to get phototexturing working with ShaderMaterial
. I'm afraid I need a little more help. I tried putting more attributes in my ShaderMaterial
like skinning:true
. I also tried altering my shaders. Where does the code go?
As I said, this should be the last step in this bounding box and afterwards, I will gladly share my code for others to use.
In webgl_custom_attributes.html
relevant lines for using texture in ShaderMaterial
are:
Uniform that holds texture:
texture: { type: "t", value: 0, texture: THREE.ImageUtils.loadTexture( "textures/water.jpg" ) },
https://github.com/mrdoob/three.js/blob/master/examples/webgl_custom_attributes.html#L120
Declaration of this uniform in fragment shader:
uniform sampler2D texture;
https://github.com/mrdoob/three.js/blob/master/examples/webgl_custom_attributes.html#L66
Getting color out of texture:
vec4 tcolor = texture2D( texture, vUv );
https://github.com/mrdoob/three.js/blob/master/examples/webgl_custom_attributes.html#L75
Declaration of texture coordinates varying in vertex and fragment shaders:
varying vec2 vUv;
https://github.com/mrdoob/three.js/blob/master/examples/webgl_custom_attributes.html#L46 https://github.com/mrdoob/three.js/blob/master/examples/webgl_custom_attributes.html#L63
Passing of texture coordinate attribute into varying in vertex shader (you don't need to mess with them, it was just in that example to have "floating" texture effect, just pass them unchanged):
vUv = uv;
https://github.com/mrdoob/three.js/blob/master/examples/webgl_custom_attributes.html#L51
Now if you just want to reuse texture that is already defined in some material in JSON, it should be enough to do something like this:
uniforms[ "texture" ].texture = geometry.materials[ 0 ].map;
Ok, great! Thank you @alteredq . I slapped some of that code for the shader in there and I got it working! I started stripping the lines that didn't apply to my needs and it actually dwindled to something very brief and concise. The worldPosition and clippingPlane stuff is for the clipping box, the rest is for the texturing:
<script type="x-shader/x-vertex" id="vertexshader2">
varying vec2 vUv;
varying vec4 worldPosition;
void main() {
worldPosition = objectMatrix * vec4( position, 1.0 );
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader2">
varying vec2 vUv;
uniform sampler2D texture;
varying vec4 worldPosition;
uniform float xClippingPlaneMax;
uniform float xClippingPlaneMin;
uniform float yClippingPlaneMax;
uniform float yClippingPlaneMin;
uniform float zClippingPlaneMax;
uniform float zClippingPlaneMin;
void main() {
vec4 tcolor = texture2D( texture, vUv );
gl_FragColor = tcolor;
if(worldPosition.x<xClippingPlaneMin) discard;
if(worldPosition.x>xClippingPlaneMax) discard;
if(worldPosition.z<zClippingPlaneMin) discard;
if(worldPosition.z>zClippingPlaneMax) discard;
if(worldPosition.y<yClippingPlaneMin) discard;
if(worldPosition.y>yClippingPlaneMax) discard;
}
</script>
I do have one other small problem. In my uniform, in js, I have:
texture: THREE.ImageUtils.loadTexture( "models/test25percentImage1.png" )
That's no good. I can't have the image hardcoded in there. And, worse, my json mesh file could point to multiple images (an obj/mtl meshes can and should have multiple images to make it look better, right?). How can I dynamically do this with multiple images without hardcoding the texture attribute in the uniform? I feel like I could read through the json file, find the different textures, and then split up all my geometries, but that sounds hard on the processor for 100's of thousands of faces. Is there a better way to do this with either three.js or webgl? Maybe some conditional logic in the shaders to point to a different texture file based on a certain attribute?
I have a scene that I render as a
ParticleSystem
or a mesh. Here's theParticleSystem
one:http://archive.cyark.org/3d/index11.php?chunks=3
I want to put in a bounding box and have the user control the extents of the bounding box. And this bounding box would double as a clipping box, meaning anything outside of this box would not be visible. I'm new to 3d rendering, but I think this concept is pretty common. My question: How do I do this?
I figure, on every render() loop, I can go through all my points and test with simple math equations if they are out of the box or not. This concept is simple, but would probably kill my computer. My second thought would be to render massive black rectangles with outward culling which would just, essentially, block things outside of the box. This would require the background to be a flat color matching my blocking rectangles, which I don't want. I'd prefer a non flat background, like the one in my example above.
Is there an element in the api to do this? I found issue #647 which was rather brief, but implies that it can be done. Can you kindly provide some more information as to how to implement this?
I gather this is a feature in webgl that doesn't really have a three.js layer to support it. Is that correct? I don't know webgl, but I can learn. How do I incorporate webgl into my three.js application?