Been using THREE JS CSS3DRenderer to render a panorama view based in this example.
I have added another CSS3DObject image which represents a GPS marker with the following code below:
var marker = document.createElement( 'img' );
marker.classList.add('gps_marker');
marker.src = 'gps_marker.jpg';
marker.width = 5;
marker.style.border = 'solid 1px red';
var object = new THREE.CSS3DObject( marker );
object.position.set( 0, 0, 30 );
scene.add( object );
Now I'd like to make the gps markers draggable around the scene around the scene. Dragcontrols doesn't seem to work with CSS3DObject. Any help would be very appreciated.
You need to use "textured" point cloud (THREE.Points) object with shader material. Use uniform to pass texture to fragment shader. Not sure that dragcontrols will work with it (will move all points at ones if so). But this is IMHO the best way to draw icons. Example:
let uniforms = {
iconSize: {
value: self.iconSize
},
color: {
value: new THREE.Color(0xffffff)
},
texture: {
value: self.textureLoader.load(url, texture => {
texture.flipY = false;
texture.needsUpdate = true;
})
}
};
let material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('point-cloud-vertex-shader').textContent,
fragmentShader: document.getElementById('point-cloud-fragment-shader').textContent,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.7,
depthTest: false,
clipping: true,
clippingPlanes: self.clippingPlanes,
});
//... create THREE.Points object with this material
shaders:
<script type="x-shader/x-vertex" id="point-cloud-vertex-shader">
uniform vec3 color;
uniform float iconSize;
uniform sampler2D texture;
void main() {
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = iconSize;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="point-cloud-fragment-shader">
uniform vec3 color;
uniform float iconSize;
uniform sampler2D texture;
void main() {
gl_FragColor = texture2D( texture, gl_PointCoord );
}
</script>
Related
I am using InstancedMesh with an custom outline shader:
<script type="x-shader/x-vertex" id="vertexShader">
uniform float offset;
void main() {
vec4 pos = modelViewMatrix * vec4( position + normal * offset, 1.0 );
gl_Position = projectionMatrix * pos;
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
uniform vec3 color;
uniform float intensity;
void main() {
gl_FragColor = vec4( color, intensity );
}
</script>
Then, I created shader material:
var uniforms = {
offset: { type: 'f', value: 0.001 },
color: { type: 'c', value: new THREE.Color(0x6B6B6B) },
intensity: { type: 'f', value: 1.0 },
};
var outlineMaterial = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
});
Then I created an InstancedMesh with a box geometry and this material. However, when I update instance matrix, the shader seems NOT be affected.
var dummy = new THREE.Object3D();
var outlineGometry = new THREE.BoxGeometry(1, 1, 1);
var outline = new THREE.InstancedMesh(outlineGometry, outlineMaterial, 1);
dummy.position.set(0, 0, 0);
dummy.rotation.set(0, Math.PI/4, 0);
dummy.scale.set(0.1, 0.1, 0.1);
dummy.updateMatrix();
outline.setMatrixAt( 0, dummy.matrix );
outline.instanceMatrix.needsUpdate = true;
Please check the fiddle link below: I updated the instance rotation and scale, but shader is NOT changed.
Fiddle Link
Do you know how to solve this problem?
Thanks.
According to the Westlangley suggestion, I can use instanceMatrix in vertex shader to solve the problem based on the document https://threejs.org/docs/#api/en/renderers/webgl/WebGLProgram
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
I have a classic mesh composed by a THREE.PlaneGeometry and a material. If I add a THREE.MeshNormalMaterial() here's the result I get :
So far, so good. But when I call my THREE.ShaderMaterial(), using an external texture, the dimension of my mesh completely changes :
I always get that weird ratio even if - like in the screenshot - my texture is a square (512x512). I just want my MaterialShader to fit inside my geometry.
Here is the code of my MaterialShader :
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 );
I don't see what I'm missing. Does anyone has an idea ? Thank you very much.
UPDATE :
Here's the fully code of my ShaderMaterial :
material = new THREE.ShaderMaterial({
uniforms:{
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new THREE.Vector2() },
u_mouse: { type: "v2", value: new THREE.Vector2() },
texture1: { type: "t", value: texture }
},
vertexShader:`
void main() {
gl_Position = vec4( position, 1.0 );
}
`,
fragmentShader:`
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
uniform sampler2D texture1;
void main(){
float pyt=3.1415926*2./3.;
float m=-1e10;
vec4 mv= vec4(0.);
vec2 xy = gl_FragCoord.xy/u_resolution.xy;
int ic=0;
for (int i=0;i<30;i++){
vec2 np=vec2(xy.x+float(i)/u_resolution.x*sin(3.14/2.) * 4.,xy.y+float(i)/u_resolution.y*cos(3.14/2.) * 4.);
float jTime = u_time*1.618;
vec4 tk=texture2D(texture1,np);
float t=tk.r*sin(jTime)+tk.g*sin(jTime+pyt)+tk.b*sin(jTime+2.*pyt)-.01*float(i);
if (t>m){m=t; mv=tk;ic=i;}
}
float sc=float(ic)/30.;
vec4 tk=texture2D(texture1,xy);
mv=sc*tk+(1.-sc)*mv;
gl_FragColor = vec4(mv.r,mv.g,mv.b,1.0);
}
`
});
UPDATE2 :
I changed my vertex shader but nothing has changed.
I might have a lead : I think this is related to my camera settings. I changed them and I've a better result. Now my texture fits into my square mesh.
Unfortunately, the scale isn't good. Since my texture is a square too, I want it to have exactly the same size than my mesh, for now it's zoomed.
How can I manage the size of my texture ? Should I do it inside my vertexShader ?
Here's my texture settings for now :
texture = new THREE.TextureLoader().load( "test5.jpg");
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
UPDATE 3 :
I found that code to apply a texture and make it fit to my mesh :
https://bl.ocks.org/duhaime/c8375f1c313587ac629e04e0253481f9
It's working but as soon as I change the example fragement shader by mine, I've no errors but the shaders become one unique color. I don't understand what I'm missing...
Try this code of the vertex shader:
void main() {
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position,1.0);
}
Reference
Simply pass the uv coordinates from the vertex shader to the fragment shader and use them there.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 0, 5);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x404040);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var iResolution = new THREE.Vector2();
var planeGeo = new THREE.PlaneBufferGeometry(5, 5);
var planeMat = new THREE.ShaderMaterial({
uniforms: {
texture: {
value: null
},
iResolution: {
value: iResolution
},
iTime: {
value: 0
}
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix *
modelViewMatrix *
vec4(position,1.0);
}
`,
fragmentShader: `
uniform sampler2D texture;
uniform float iTime;
uniform vec2 iResolution;
varying vec2 vUv;
void main() {
float pyt=3.1415926*2./3.;
float m=-1e10;//very negitive start value for maximisation algorithm.
vec4 mv= vec4(0.);//lank starting value of max so far
vec2 xy = vUv;
int ic=0;//stores smear distance
for (int i=0;i<30;i++){
//point offset on a circle
vec2 np=vec2(xy.x+float(i)/iResolution.x*sin(iTime),xy.y+float(i)/iResolution.y*cos(iTime));
//colour cycles faster than position
float jTime = iTime*1.618;
//get neerby point
vec4 tk=texture2D(texture,np);
// and if its colourfull enough, use that
float t=tk.r*sin(jTime)+tk.g*sin(jTime+pyt)+tk.b*sin(jTime+2.*pyt)-.01*float(i);
if (t>m){m=t; mv=tk;ic=i;}
}
//mix smeared with background depending ondistance
float sc=float(ic)/30.;
vec4 tk=texture2D(texture,xy);
mv=sc*tk+(1.-sc)*mv;
gl_FragColor = vec4(mv.rgb,1.0);
}
`
});
var textureLoader = new THREE.TextureLoader();
textureLoader.load("https://threejs.org/examples/textures/UV_Grid_Sm.jpg", tex => {
planeMat.uniforms.texture.value = tex;
planeMat.uniforms.texture.value.needsUpdate = true;
iResolution.set(tex.image.width, tex.image.height);
planeMat.needsUpdate = true;
console.log(texture);
});
var plane = new THREE.Mesh(planeGeo, planeMat);
scene.add(plane);
var clock = new THREE.Clock();
var time = 0;
render();
function render() {
requestAnimationFrame(render);
time += clock.getDelta();
planeMat.uniforms.iTime.value = time;
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
I add a json model with glow effect into the scene.
As follows:
I try to rotate the json model automatically.
However, it looks weird when it is rotating.
The glow effect of the model does not work.
I assume that the position of the json model does not be changed when this model is rotating. As the result, the viewVector.value of the ShaderMaterial is constant when this model is rotating(I do not change position of the camera).
if(jsonMesh){
jsonMesh.rotation.y += 0.1;
jsonMesh.material.uniforms.viewVector.value =
new THREE.Vector3().subVectors( camera.position, jsonMesh.position );
}
This is the Three.ShaderMaterial.
VertexShader and FragmentShader
<script id="vertexShader" type="x-shader/x-vertex">
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main()
{
vec3 vNormal = normalize( normalMatrix * normal );
vec3 vNormel = normalize( normalMatrix * viewVector );
intensity = pow( c - dot(vNormal, vNormel), p );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec3 glowColor;
varying float intensity;
void main()
{
vec3 glow = glowColor * intensity;
gl_FragColor = vec4( glow, 1.0 );
}
</script>
Three.ShaderMaterial.
var customMaterial = new THREE.ShaderMaterial(
{
uniforms:
{
"c": { type: "f", value: 1.0 },
"p": { type: "f", value: 1.4 },
glowColor: { type: "c", value: new THREE.Color(0xffff00) },
viewVector: { type: "v3", value: camera.position }
},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
side: THREE.FrontSide,
blending: THREE.AdditiveBlending,
transparent: true
}
);
How should I modify the code in this case?
Here is the Demo and source code.
You can use built in three.js functions for this. Instead of using the camera position, I chose to show you how to set a light source position in the world. That way you can match the light source on your custom shader to any light sources you plan to add later to your 3d world. Feel free to change the worldLightPoint value to camera.position instead of new THREE.Vector3(100,100,100). and in that case the effect will remain constant with the camera position.
var v = new THREE.Vector3();
//var worldLightPoint = camera.position;
var worldLightPoint = new THREE.Vector3(100,100,100);
function update()
{
controls.update();
stats.update();
if(jsonMesh){
jsonMesh.rotation.y += 0.1;
jsonMesh.material.uniforms.viewVector.value = jsonMesh.worldToLocal(v.copy(worldLightPoint));
}
}
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