I use a ShaderMaterial that is a complete copy of the default Phong shader (I use shaderChunks).
My problem is that the envMap is flipped over X...
Here is what I get:
And what I want:
I tried to access cubeCamera.renderTarget.texture to use repeat.x = -1 and wrapS = THREERepeatWrapping.RepeatWrapping but it does absolutely nothing...
var cubeCamera = new THREE.CubeCamera(0.01, 500, 256);
scene.add(cubeCamera);
var sphereMaterial = new THREE.ShaderMaterial({
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib.common,
THREE.UniformsLib.aomap,
THREE.UniformsLib.lightmap,
THREE.UniformsLib.emissivemap,
THREE.UniformsLib.bumpmap,
THREE.UniformsLib.normalmap,
THREE.UniformsLib.displacementmap,
THREE.UniformsLib.fog,
THREE.UniformsLib.lights,
THREE.UniformsLib.shadowmap,
{
emissive: {
type: "c",
value: new THREE.Color(0x000000)
},
specular: {
type: "c",
value: new THREE.Color(0x111111)
},
shininess: {
type: "f",
value: 30
},
envMap: {
type: "t",
value: cubeCamera.renderTarget
}
}
]),
vertexShader: require("../../shaders/phong.vert.glsl"),
fragmentShader: require("../../shaders/phong.frag.glsl"),
blending: THREE.AdditiveBlending,
depthWrite: false,
transparent: true,
opacity: 1,
lights: true
});
sphereMaterial.envMap = true;
EDIT:
JSFIDDLE example
To get the reflection you are after, set
sphereShaderMaterial.uniforms.flipEnvMap.value = 1;
I'm not sure when this actually gets set on the MeshPhongMaterial, I suspect somewhere in the WebGLRenderer it does some checks and sets this. The WebGLRenderer does many things to the different types of materials which makes them a little hard to just extend easily.
Related
I have some text rendering over a background quad. Let's call this a 'label'. Both are positioned at the same point which causes z-fighting.
I'd like to promote the text to avoid z-fighting using polygon offset.
This is how I add the polygon offset to the text material:
const material = new THREE.RawShaderMaterial(
CreateMSDFShader({
map: this.glyphs,
opacity: opt.opacity ?? 1,
alphaTest: (opt.opacity ?? 1) < 1 ? 0.001 : 1,
color: opt.colour ?? '#ffffff',
transparent: opt.transparent ?? true,
glslVersion: opt.renderMode === 'webgl' ? THREE.GLSL1 : THREE.GLSL3,
side: opt.side ?? THREE.DoubleSide,
depthFunc: opt.depthFunc ?? THREE.LessEqualDepth,
depthTest: true,
depthWrite: false,
polygonOffset: true,
polygonOffsetUnits: -1.0,
polygonOffsetFactor: -4.0,
})
);
const mesh = new THREE.Mesh(geom, material);
and this is the background material:
if (tableOptions.background) {
const geometry = new THREE.PlaneGeometry(1, 1, 1, 1);
const backgroundMaterial = new ActivatableMaterial(
{
color: new THREE.Color(tableOptions.backgroundColour),
toneMapped: false,
opacity: 1,
alphaTest: 0.001,
transparent: true,
},
{
activationColour: new THREE.Color(tableOptions.activationColour),
}
);
this.background = buildUIObject(
Actions.ExpandLabel as Action,
geometry,
backgroundMaterial
);
setName(this.background, 'Background');
this.tableGroup.add(this.background);
}
The polygon offset just isn't working (using Chrome). The text disappears behind the background quad as I orbit the camera around, and reappears at random. The labels are always facing the camera (using lookAt).
What could stop the polygon offset from working?
The labels are rendering to a renderpass with a render taget set up as follows:
const pars = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
stencilBuffer: false,
depthBuffer: false,
};
this.renderTargetBuffer = new THREE.WebGLRenderTarget(
resolution.x,
resolution.y,
pars
);
this.renderTargetBuffer.texture.name = 'RenderTargetBuffer';
this.renderTargetBuffer.texture.generateMipmaps = false;
I'm assuming that because the polygonOffset is a state thing it doesn't matter that this is a RawShaderMaterial. Is that a safe assumption?
Edit: I have added the opposite polygonOffset to the background mesh separately and again it doesn't work.
Polygon offset wasn't the best solution in this case. I switched to maintaining a custom order for the labels based on distance to the camera. That way I could force the text render order to be greater than the background.
I have written a points example according to official example, but the result is not happy:
The point is Rectangular, rather than circular!
Here is my material code:
let material = new THREE.ShaderMaterial({
uniforms: {
uColor: { value: new THREE.Color(0xffffff) },
uPointTexture: { value: new THREE.TextureLoader().load(require('./res/spark1')) }
},
vertexShader: _me.vertex_shader,
fragmentShader: _me.fragment_shader,
transparent: true
})
After some try, like: blending: THREE.AdditiveBlending, fog: false...I found is related to depthTest property, after I set false, result is below:
see, black square disappeared!Here is final code:
let material = new THREE.ShaderMaterial({
uniforms: {
uColor: { value: new THREE.Color(0xffffff) },
uPointTexture: { value: new THREE.TextureLoader().load(require('./res/spark1')) }
},
vertexShader: _me.vertex_shader,
fragmentShader: _me.fragment_shader,
depthTest: false,
transparent: true
})
I have a renderer:
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: false,
alpha: true
})
And a ShaderMaterial:
const material = new THREE.ShaderMaterial({
uniforms: { ... },
transparent: true,
blending: THREE.NormalBlending,
vertexShader: renderVert,
fragmentShader: renderFrag
})
And i have a fragment shader (simplified here):
void main() {
gl_FragColor = vec4(vec3(0.0), 0.2);
}
But the alpha is not taken into account and the shader renders all colours fully opaque. Also with another blending mode still doesn't work as expected. Why?
I have a .obj and .mtl which load textures from .tga files. This all seems to work / look fine in 3D modeling software, but when loaded with three.js (THREE.WebGLRenderer({ antialias: true })) some of the object's children, (specifically between the knees and the calves, but not, for example, the pockets and legs), seem to have a transparent jagged gap / empty triangles.
(Tried turning on antialiasing, changing the overdraw value, turning off transparency, etc.)
Loader function:
threejsHelper.helpers.loadObject('objects/pants/MaleBaggyPants.obj', 'objects/pants/MaleBaggyPants.mtl', {
blinn1SG: (new THREE.MeshPhongMaterial({ color: 0xffffff, transparent: false, opacity: 1.0, overdraw: 0.5, map: threejsHelper.helpers.loadTexture(app.manager, 'objects/pants/button.tga') })),
blinn2SG: (new THREE.MeshPhongMaterial({ color: 0xffffff, transparent: false, opacity: 1.0, overdraw: 0.5, map: threejsHelper.helpers.loadTexture(app.manager, 'objects/pants/back.tga') })),
blinn4SG: (new THREE.MeshPhongMaterial({ color: 0xffffff, transparent: false, opacity: 1.0, overdraw: 0.5, map: threejsHelper.helpers.loadTexture(app.manager, 'objects/pants/front.tga') })),
blinn5SG: (new THREE.MeshPhongMaterial({ color: 0xffffff, transparent: false, opacity: 1.0, overdraw: 0.5, map: threejsHelper.helpers.loadTexture(app.manager, 'objects/pants/pocket.tga') })),
blinn6SG: (new THREE.MeshPhongMaterial({ color: 0xffffff, transparent: false, opacity: 1.0, overdraw: 0.5, map: threejsHelper.helpers.loadTexture(app.manager, 'objects/pants/back.tga') })),
blinn7SG: (new THREE.MeshPhongMaterial({ color: 0xffffff, transparent: false, opacity: 1.0, overdraw: 0.5, map: threejsHelper.helpers.loadTexture(app.manager, 'objects/pants/front.tga') }))
}, function (object) {
object.rotation.x = (-90*Math.PI/180);
object.rotation.z = (-90*Math.PI/180);
app.scene.add(object);
app.ready = true;
});
Helper functions:
...
loadTexture: function (manager, path) {
var texture;
if (path.split('.').pop() == 'tga') {
var loader = new THREE.TGALoader();
texture = loader.load(path);
} else {
texture = new THREE.Texture();
var loader = new THREE.ImageLoader(manager);
loader.load(path, function (image) {
texture.image = image;
texture.needsUpdate = true;
});
}
return texture;
},
loadMaterial: function (mtlPath, textures, complete) {
var loader = new THREE.MTLLoader();
loader.load(mtlPath, function (materials) {
materials.preload();
if (!!materials.materials) {
for (var key in materials.materials) {
if (key in textures) {
materials.materials[key] = textures[key];
}
}
}
complete(materials);
});
},
loadObject: function (objPath, mtlPath, textures, complete) {
app.helpers.loadMaterial(mtlPath, textures, function (materials) {
var loader = new THREE.OBJLoader();
loader.setMaterials(materials);
loader.load(objPath, function (object) {
complete(object);
});
});
},
...
You seem to have extra vertices along the seem on both the objects which I think will screw with the triangulation.
Remove these vertices that are not needed and I'm 99% sure it will fix your problem.
I also noticed the model is at a very small scale.
I had the same problem. Some triangle didn't properly render on Threejs while it properly rendered on my OpenGL app.
I solved this trouble by using Uint32Array instead of Uint16Array to define the index of the geometry. I just put this line of code and everything worked like a charm.
const indices = new Uint32Array(faceDict.index);
I would like to modify one object within a mesh using perlin noise. How I am doing this right now...
I am creating the objects and adding them to a 3d object...
spheres = new THREE.Object3D();
for ( var i = 0; i < 40; i ++ ) {
var ball = new THREE.Mesh(new THREE.SphereGeometry(20,20,20), material);
ball.position.x = Math.random() * 600 - 300;
ball.position.y = Math.random() * 600 - 300;
ball.position.z = Math.random() * 600 - 300;
spheres.add(ball);
}
scene.add(spheres);
I am using a shader material...
material = new THREE.ShaderMaterial({
uniforms: {
time: {
type: "f",
value: 0.0
},
noisemax: {
type: "f",
value: 100.0
},
transparent: true
},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
});
The shader material works on all 40 balls no problem. What I would like to do is change the shader say for spheres.children[0]. Is it possible to change the perlin noise values (noiseMax) for one object or does it by nature effect the material for all objects using the material?
You have a couple of options.
The easy way is to create and use a separate ShaderMaterial for each item that differs, and you can set its uniform easily. e.g.
var firstMaterial = new THREE.ShaderMaterial({
uniforms: { noisemax: { type: 'f', value: 3}}
});
var secondMaterial = new THREE.ShaderMaterial({
uniforms: { noisemax: { type: 'f', value: 10}}
});
var firstBall = new THREE.Mesh(new THREE.SphereGeometry(20,20,20), firstMaterial);
var secondBall = new THREE.Mesh(new THREE.SphereGeometry(20,20,20), secondMaterial);
// or in your case
spheres.children[0].material = secondMaterial;
Alternatively and probably preferably for your situation (at least from a performance standpoint) is that you should change noisemax to an attribute, that way you can have a separate value per object.
You will have to remember that attributes are per vertex, so you'll need to duplicate the values for all vertices belonging to each object. This will complicate things a little bit.
Edit: To reduce memory usage, you can use THREE.InstancedBufferGeometry with THREE.InstancedBufferAttribute