I'm trying to use the Logarithmic Depth Buffer with an Orthographic camera, and encountering some interesting results. I've constructed a sample here: http://jsfiddle.net/TheJim01/05up96m0/
The renderer on the left (the red knot) uses a standard linear depth buffer (1 to 1000). Selecting Perspective or Orthographic will re-render the scene with the selected camera type, and both appear to work as expected.
The renderer on the right (the green knot) uses a logarithmic depth buffer (1e-6 to 1e27). As with the left sample, selecting a camera will re-render the scene with that camera. But in this case, only the Perspective camera works as expected. When Orthographic is selected, the knot "unties". It looks almost like it's not considering the depth buffer at all.
Am I using it wrong, or is something else going on here?
var WIDTH = 250,
HEIGHT = 250,
BACKGROUND = 0xcccccc;
var gl1 = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: false
}),
gl2 = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true
}),
scene1 = new THREE.Scene(),
scene2 = new THREE.Scene(),
s1pCam = new THREE.PerspectiveCamera(
28,
WIDTH / HEIGHT,
1,
1000
),
s1oCam = new THREE.OrthographicCamera(-1 * (WIDTH / HEIGHT),
1 * (WIDTH / HEIGHT),
1, -1,
1,
1000),
aLight1 = new THREE.AmbientLight(0x333333),
dLight1 = new THREE.DirectionalLight(0xffffff, 0.75);
s1pCam.position.set(0, 0, 100);
s1pCam.lookAt(scene1.position);
s1oCam.position.set(0, 0, 100);
s1oCam.lookAt(scene1.position);
dLight1.position.set(0, 0, 100);
dLight1.lookAt(new THREE.Vector3(0, 0, -1));
// calculate ortho frustum
var modelCenter = new THREE.Vector3(),
tmpCamPosition = s1pCam.position.clone(),
camTarget = new THREE.Vector3(),
radFOV = (Math.PI / 180.) * s1pCam.fov;
modelCenter.sub(camTarget);
tmpCamPosition.sub(camTarget);
var projectedLocation = modelCenter.projectOnVector(tmpCamPosition);
var distance = tmpCamPosition.distanceTo(projectedLocation);
var halfHeight = Math.tan(radFOV / 2.) * distance;
var halfWidth = halfHeight * s1pCam.aspect;
s1oCam.left = -halfWidth;
s1oCam.right = halfWidth;
s1oCam.top = halfHeight;
s1oCam.bottom = -halfHeight;
s1oCam.zoom = s1pCam.zoom;
s1oCam.updateProjectionMatrix();
var s2pCam = s1pCam.clone(),
s2oCam = s1oCam.clone(),
aLight2 = aLight1.clone(),
dLight2 = dLight1.clone();
s2pCam.near = 1e-6;
s2pCam.far = 1e27;
s2oCam.near = 1e-6;
s2oCam.far = 1e27;
scene1.add(s1pCam);
scene1.add(s1oCam);
scene1.add(aLight1);
scene1.add(dLight1);
scene1.add(new THREE.Mesh(new THREE.TorusKnotGeometry(10, 4, 100, 32), new THREE.MeshPhongMaterial({
color: 'red'
})));
scene2.add(s2pCam);
scene2.add(s2oCam);
scene2.add(aLight2);
scene2.add(dLight2);
scene2.add(new THREE.Mesh(new THREE.TorusKnotGeometry(10, 4, 100, 32), new THREE.MeshPhongMaterial({
color: 'green'
})));
document.getElementById("view1").appendChild(gl1.domElement);
document.getElementById("view2").appendChild(gl2.domElement);
gl1.setSize(WIDTH, HEIGHT);
gl1.setClearColor(BACKGROUND);
gl2.setSize(WIDTH, HEIGHT);
gl2.setClearColor(BACKGROUND);
gl1.render(scene1, s1pCam);
gl2.render(scene2, s2pCam);
function handleCameraChanges(e) {
debugger;
if (gl1 && e.target.id.indexOf("1") !== -1) {
gl1.render(scene1, (e.target.id.indexOf("p") !== -1) ? s1pCam : s1oCam);
} else if (gl2) {
gl2.render(scene2, (e.target.id.indexOf("p") !== -1) ? s2pCam : s2oCam);
}
}
document.getElementById("v1p").addEventListener("click", handleCameraChanges);
document.getElementById("v1o").addEventListener("click", handleCameraChanges);
document.getElementById("v2p").addEventListener("click", handleCameraChanges);
document.getElementById("v2o").addEventListener("click", handleCameraChanges);
.view {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.min.js"></script>
<fieldset id="view1" class="view">
<legend>Standard Depth Buffer</legend>
<label><input id="v1p" type="radio" name="cameraMode1" checked />Perspective</label>
<label><input id="v1o" type="radio" name="cameraMode1" />Orthographic</label><br>
</fieldset>
<fieldset id="view2" class="view">
<legend>Logarithmic Depth Buffer</legend>
<label><input id="v2p" type="radio" name="cameraMode2" checked />Perspective</label>
<label><input id="v2o" type="radio" name="cameraMode2" />Orthographic</label><br></fieldset>
This has been resolved in PR 17442, set for release in r109.
r109 was released on 30 September, 2019.
Here is the exact same example as above, using r109. Thanks to everyone involved in the fix!
var WIDTH = 250,
HEIGHT = 250,
BACKGROUND = 0xcccccc;
var gl1 = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: false
}),
gl2 = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true
}),
scene1 = new THREE.Scene(),
scene2 = new THREE.Scene(),
s1pCam = new THREE.PerspectiveCamera(
28,
WIDTH / HEIGHT,
1,
1000
),
s1oCam = new THREE.OrthographicCamera(-1 * (WIDTH / HEIGHT),
1 * (WIDTH / HEIGHT),
1, -1,
1,
1000),
aLight1 = new THREE.AmbientLight(0x333333),
dLight1 = new THREE.DirectionalLight(0xffffff, 0.75);
s1pCam.position.set(0, 0, 100);
s1pCam.lookAt(scene1.position);
s1oCam.position.set(0, 0, 100);
s1oCam.lookAt(scene1.position);
dLight1.position.set(0, 0, 100);
dLight1.lookAt(new THREE.Vector3(0, 0, -1));
// calculate ortho frustum
var modelCenter = new THREE.Vector3(),
tmpCamPosition = s1pCam.position.clone(),
camTarget = new THREE.Vector3(),
radFOV = (Math.PI / 180.) * s1pCam.fov;
modelCenter.sub(camTarget);
tmpCamPosition.sub(camTarget);
var projectedLocation = modelCenter.projectOnVector(tmpCamPosition);
var distance = tmpCamPosition.distanceTo(projectedLocation);
var halfHeight = Math.tan(radFOV / 2.) * distance;
var halfWidth = halfHeight * s1pCam.aspect;
s1oCam.left = -halfWidth;
s1oCam.right = halfWidth;
s1oCam.top = halfHeight;
s1oCam.bottom = -halfHeight;
s1oCam.zoom = s1pCam.zoom;
s1oCam.updateProjectionMatrix();
var s2pCam = s1pCam.clone(),
s2oCam = s1oCam.clone(),
aLight2 = aLight1.clone(),
dLight2 = dLight1.clone();
s2pCam.near = 1e-6;
s2pCam.far = 1e27;
s2oCam.near = 1e-6;
s2oCam.far = 1e27;
scene1.add(s1pCam);
scene1.add(s1oCam);
scene1.add(aLight1);
scene1.add(dLight1);
scene1.add(new THREE.Mesh(new THREE.TorusKnotGeometry(10, 4, 100, 32), new THREE.MeshPhongMaterial({
color: 'red'
})));
scene2.add(s2pCam);
scene2.add(s2oCam);
scene2.add(aLight2);
scene2.add(dLight2);
scene2.add(new THREE.Mesh(new THREE.TorusKnotGeometry(10, 4, 100, 32), new THREE.MeshPhongMaterial({
color: 'green'
})));
document.getElementById("view1").appendChild(gl1.domElement);
document.getElementById("view2").appendChild(gl2.domElement);
gl1.setSize(WIDTH, HEIGHT);
gl1.setClearColor(BACKGROUND);
gl2.setSize(WIDTH, HEIGHT);
gl2.setClearColor(BACKGROUND);
gl1.render(scene1, s1pCam);
gl2.render(scene2, s2pCam);
function handleCameraChanges(e) {
debugger;
if (gl1 && e.target.id.indexOf("1") !== -1) {
gl1.render(scene1, (e.target.id.indexOf("p") !== -1) ? s1pCam : s1oCam);
} else if (gl2) {
gl2.render(scene2, (e.target.id.indexOf("p") !== -1) ? s2pCam : s2oCam);
}
}
document.getElementById("v1p").addEventListener("click", handleCameraChanges);
document.getElementById("v1o").addEventListener("click", handleCameraChanges);
document.getElementById("v2p").addEventListener("click", handleCameraChanges);
document.getElementById("v2o").addEventListener("click", handleCameraChanges);
.view {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
<fieldset id="view1" class="view">
<legend>Standard Depth Buffer</legend>
<label><input id="v1p" type="radio" name="cameraMode1" checked />Perspective</label>
<label><input id="v1o" type="radio" name="cameraMode1" />Orthographic</label><br>
</fieldset>
<fieldset id="view2" class="view">
<legend>Logarithmic Depth Buffer</legend>
<label><input id="v2p" type="radio" name="cameraMode2" checked />Perspective</label>
<label><input id="v2o" type="radio" name="cameraMode2" />Orthographic</label><br></fieldset>
Related
Screen capture
I tried to make a 3/4 cylinder surface, and I made it from extruting by a ellipe path,
but when I tried to load a texture to the surface, It does not as I exprected: uniformly painted to the surface, it's stretched
I know it's about texture projection, But I dont know how to set options.
class EllipseCurve3 extends THREE.Curve {
ellipse = null
constructor (ellipse) {
super()
this.ellipse = ellipse
}
getPoint(t, optionalTarget = new THREE.Vector3()) {
const point = this.ellipse.getPoint(t, optionalTarget)
return new THREE.Vector3(
point.x,
point.y,
0
)
}
}
// Scene
const scene = new THREE.Scene();
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.moveTo(0, 1);
shape.lineTo(50, 1);
shape.moveTo(50, 0);
shape.lineTo(0, 0);
// var curve = new THREE.CatmullRomCurve3([
// new THREE.Vector3(0, 0, 50),
// new THREE.Vector3(-50, 0, 0),
// new THREE.Vector3(0, 0, -50)
// ]);
const arc = new THREE.EllipseCurve(
0,
0, // ax, aY
100,
100, // xRadius, yRadius
0,
1.5 * Math.PI, // aStartAngle, aEndAngle
false, // aClockwise
0 // aRotation
),
path = new EllipseCurve3(arc)
geometry = new THREE.ExtrudeGeometry(shape, {
bevelEnabled: false,
extrudePath: path,
steps: 50,
depth: 5,
amount: 20,
material: 0,
extrudeMaterial: 1
});
// Set up lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const axesHelper = new THREE.AxesHelper(500);
scene.add(axesHelper);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);
directionalLight.position.set(100, 200, 100); // x, y, z
scene.add(directionalLight);
// Camera
const width = 200;
const height = width * (window.innerHeight / window.innerWidth);
const camera = new THREE.OrthographicCamera(
width / -2, // left
width / 2, // right
height / 2, // top
height / -2, // bottom
0.1, // near
1000 // far
);
camera.position.set(400, 400, 400);
camera.lookAt(0, 0, 0);
// Renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
renderer.render(scene, camera);
// Add it to HTML
document.body.appendChild(renderer.domElement);
var textureLoader = new THREE.TextureLoader();
textureLoader.crossOrigin = true;
const picture = 'https://threejs.org/examples/textures/uv_grid_opengl.jpg'
textureLoader.load(picture, function(texture) {
// repeat pattern
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
// zoom in on pattern
texture.repeat.set(.01, .01);
// assign texture via MeshBasicMaterial
var material = new THREE.MeshBasicMaterial({
map: texture,
needsUpdate: true,
// transparent: true,
// premultipliedAlpha: true,
// side: THREE.DoubleSide,
// blending: THREE.AdditiveBlending
});
// var material = new THREE.MeshPhongMaterial({ color: 0x0048ff });
var mesh = new THREE.Mesh(geometry, material)
mesh.rotation.x = Math.PI / 2
// mesh.rotation.z = Math.PI / 2
scene.add(mesh)
scene.add(cube)
})
function render() {
renderer.render(scene, camera);
// Rotate out group
// svgGroup.rotation.y -= 0.005
controls.update();
requestAnimationFrame(render);
}
render();
Code here
https://codepen.io/mike-xu/pen/RwQeEXJ
"I know it's about texture projection" - It's about computing UV, based on vertices coordinates.
CylinderGeometry also may help to achieve the result you described. With less code, and more convenient and predictable way.
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three#0.136.0";
import {OrbitControls} from "https://cdn.skypack.dev/three#0.136.0/examples/jsm/controls/OrbitControls.js";
console.clear();
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 10, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", event => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
let controls = new OrbitControls(camera, renderer.domElement);
scene.add(new THREE.AxesHelper(10));
let g = new THREE.CylinderGeometry(5, 5, 5, 100, 20, true, 0, Math.PI * 1.5);
let m = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
map: new THREE.TextureLoader().load(
"https://threejs.org/examples/textures/uv_grid_opengl.jpg",
tex => {
tex.wrapS = tex.wrapT = THREE.MirroredRepeatWrapping;
tex.repeat.set(3, 1);
}
)
});
let c = new THREE.Mesh(g, m);
scene.add(c);
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
</script>
question reference
I want to implement an animation.
The animation should be a line move to another line. There will be some deformation in the process of the line moving
There is a correspondence between the points of the two lines.
I can use the basic line of three.js to animate one line moving to another.
<script type="module">
import * as THREE from '../../build/three.module.js'
import { OrbitControls } from '../jsm/controls/OrbitControls.js'
function main() {
var scene = new THREE.Scene()
const geometry = new THREE.BufferGeometry().setAttribute('position',
new THREE.Float32BufferAttribute([-2, 0, 0, -0.5, 0, -0.5, 0, 0, -2], 3))
const geometry1 = new THREE.BufferGeometry().setAttribute('position',
new THREE.Float32BufferAttribute([5, 0, 0, 1, 1, 1, 0, 0, 5], 3))
geometry.morphAttributes.position = [
geometry1.attributes.position,
]
var material = new THREE.LineBasicMaterial({
color: 0x0000ff,
})
var mesh = new THREE.Line(geometry, material)
mesh.morphTargetInfluences[0] = 0
scene.add(mesh)
/**
*
*/
var width = window.innerWidth
var height = window.innerHeight
var k = width / height
var s = 10
var camera = new THREE.OrthographicCamera(
-s * k,
s * k,
s,
-s,
-1500,
1500
)
camera.position.set(10, 10, 10)
camera.lookAt(10, 0, 0)
/**
*
*/
var renderer = new THREE.WebGLRenderer()
// window.renderer = renderer;
renderer.setSize(width, height)
renderer.setClearColor(0xb9d3ff, 1)
document.body.appendChild(renderer.domElement)
// renderer.render(scene, camera);
new OrbitControls(camera, renderer.domElement)
var clock = new THREE.Clock()
const axes = new THREE.AxesHelper(10)
scene.add(axes)
let flag = false
let index = 0
function render() {
renderer.render(scene, camera)
requestAnimationFrame(render)
if (index <0) {
flag = true
} else if (index > 1) {
flag = false
}
if (flag) {
index += 0.01
} else {
index -= 0.01
}
mesh.morphTargetInfluences[0] = index
console.log(flag, index)
}
render()
}
main()
</script>
Now I want to use the LineGeometry of three.js because this line has a width property. How to use the morphtargets with LineGeometry?
If the LineGeometry can't do it,any other bufferGeometry to replace LineGeometry?
Three.js 136.
<script type="module">
import * as THREE from '../../build/three.module.js'
import { OrbitControls } from '../jsm/controls/OrbitControls.js'
import { Line2 } from '../jsm/lines/Line2.js'
import { LineMaterial } from '../jsm/lines/LineMaterial.js'
import { LineGeometry } from '../jsm/lines/LineGeometry.js'
function main() {
var scene = new THREE.Scene()
const geometry2 = new LineGeometry()
geometry2.setPositions([-3, 0, 0, -0.5, 0, -0.5, 0, 0, -2])
console.log(geometry2)
const geometry1 = new LineGeometry()
geometry1.setPositions([-3, 0, 0, -1, 0, -1, 0, 0, -3])
geometry2.morphAttributes.position = [geometry1.attributes.position]
let matLine = new LineMaterial({
color: '#ffdb2d',
linewidth: 5, // in world units with size attenuation, pixels otherwise
})
let line = new Line2(geometry2, matLine)
line.morphTargetInfluences[0] = 0
scene.add(line)
/**
*/
var width = window.innerWidth
var height = window.innerHeight
var k = width / height
var s = 5
var camera = new THREE.OrthographicCamera(
-s * k,
s * k,
s,
-s,
-1500,
1500
)
camera.position.set(5, 5, 5)
camera.lookAt(5, 0, 0)
var renderer = new THREE.WebGLRenderer()
// window.renderer = renderer;
renderer.setSize(width, height)
renderer.setClearColor(0xb9d3ff, 1)
document.body.appendChild(renderer.domElement)
// renderer.render(scene, camera);
new OrbitControls(camera, renderer.domElement)
var clock = new THREE.Clock()
const axes = new THREE.AxesHelper(10)
scene.add(axes)
let flag = false
let index = 0
function render() {
renderer.render(scene, camera)
requestAnimationFrame(render)
if (index < 0) {
flag = true
} else if (index > 1) {
flag = false
}
if (flag) {
index += 0.01
} else {
index -= 0.01
}
line.morphTargetInfluences[0] = index
matLine.resolution.set(window.innerWidth, window.innerHeight) // resolution of the viewport
}
render()
}
main()
</script>
Use TubeGeometry:
const curve = new THREE.CatmullRomCurve3([new THREE.Vector3(-2, 0, 0), new THREE.Vector3(-0.5, 0, -0.5), new THREE.Vector3(0, 0, -2)])
var tubeGeometry = new THREE.TubeGeometry(curve, 100, 0.05, 50, false);
const curve1 = new THREE.CatmullRomCurve3([new THREE.Vector3(0, 0, 5,), new THREE.Vector3( 1, 1, 1,), new THREE.Vector3(5, 0, 0,)])
var tubeGeometry1 = new THREE.TubeGeometry(curve1, 100, 0.05, 50, false);
tubeGeometry.morphAttributes.position = [
tubeGeometry1.attributes.position,
]
var tubeMaterial = new THREE.MeshBasicMaterial({
side:THREE.DoubleSide,
color:'#ffffff',
});
var tube = new THREE.Mesh(tubeGeometry, tubeMaterial);
tube.morphTargetInfluences[0] = 0
scene.add(tube)
In the render function:
tube.morphTargetInfluences[0] += 0.01
I have a project that is using Three.js and imgui-js. I’m trying to update to the latest version of each but I'm running into issues (they were last updated around February of 2020).
To initialize the two libraries I am calling:
await ImGui.default();
ImGui.CreateContext();
ImGui_Impl.Init(canvas);
ImGui.StyleColorsDark();
const clear_color = new ImGui.ImVec4(0.3, 0.3, 0.3, 1.00);
const renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.gammaFactor = 2.2;
renderer.physicallyCorrectLights = true;
renderer.outputEncoding = THREE.sRGBEncoding;
I then add some objects to the three scene:
const scene = new THREE.Scene();
let floorDim = 1000;
var grid = new THREE.GridHelper(floorDim, 200, 'orange', 'white');
grid.name = "grid";
grid.material.opacity = 1.0;
grid.material.transparent = true;
grid.material.color.convertGammaToLinear(2.2);
grid.position.set(0, 2, 0);
scene.add(grid);
var subgrid = new THREE.GridHelper(floorDim, 600, 'grey', 'grey');
subgrid.name = "subgrid";
subgrid.material.opacity = 1.0;
subgrid.material.color.convertGammaToLinear(2.2);
subgrid.material.transparent = true;
subgrid.position.set(0, 1, 0);
scene.add(subgrid);
const light = new THREE.DirectionalLight(0xffffff, 0.8);
light.position.set(0, 100, -50);
light.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(light);
const box_1_mesh = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 5), new THREE.MeshLambertMaterial({ color:0x970000 }));
box_1_mesh.position.set(0, 5, 0);
scene.add(box_1_mesh);
const box_2_mesh = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 5), new THREE.MeshLambertMaterial({ color:0x333000 }));
box_2_mesh.position.set(-2, 6, 7);
scene.add(box_2_mesh);
const camera = new THREE.PerspectiveCamera(50, canvas.width / canvas.height, 2, 10000);
camera.position.set(0, 10, -25);
camera.updateProjectionMatrix();
camera.lookAt(0, 0, 0);
scene.add(camera);
And call an update loop that looks like:
function _loop(time) {
ImGui_Impl.NewFrame(time);
ImGui.NewFrame();
ImGui.SetNextWindowPos(new ImGui.ImVec2(20, 20), ImGui.Cond.FirstUseEver);
ImGui.SetNextWindowSize(new ImGui.ImVec2(294, 140), ImGui.Cond.FirstUseEver);
ImGui.Begin("Visblility Toggles:");
ImGui.Checkbox("box_1 (near)", (value = box_1_mesh.visible) => box_1_mesh.visible = value);
ImGui.Checkbox("box_2 (distant)", (value = box_2_mesh.visible) => box_2_mesh.visible = value);
ImGui.Checkbox("grid", (value = grid.visible) => grid.visible = value);
ImGui.Checkbox("subgrid", (value = subgrid.visible) => subgrid.visible = value);
ImGui.End();
ImGui.EndFrame();
ImGui.Render();
renderer.setClearColor(new THREE.Color(clear_color.x, clear_color.y, clear_color.z), clear_color.w);
renderer.setSize(canvas.width, canvas.height);
camera.aspect = canvas.width / canvas.height;
camera.updateProjectionMatrix();
renderer.render(scene, camera);
ImGui_Impl.RenderDrawData(ImGui.GetDrawData());
renderer.state.reset();
window.requestAnimationFrame(_loop);
}
After updating to the latest versions of these libraries, I am getting issues where objects in the scene aren't rendering as expected.
For example, I am adding 2 GridHelpers to the scene, but for some reason only one gets displayed. Also, if the visibility of the different Three objects is toggled in the scene, for example toggling the display of one of the grids off/on, when toggled back on the object doesn't display again (and sometimes other objects disappear).
Another issue occurring is that more complex meshs' materials will have an unexpected transparency, but with the simple boxes I am adding in this example, that issue doesn’t appear to be occurring. Although, it might be relevant that the look of the boxes is changing based on whether the ImGui_Impl.RenderDrawData call is made.
I have found that if I comment out the call to ImGui_Impl.RenderDrawData(ImGui.GetDrawData()), the scene displays as expected with both grid helpers showing up so I suspect that there might be some internal conflict that is now occurring between the two libraries usages of webGl. Although it's definitely possible that I have set things up incorrectly or I am missing a call that is required.
To help demonstrate the issue, I’ve created a snippet that replicates the issue: https://codepen.io/bpeake/pen/KKNNQre.
(async function() {
await ImGui.default();
const canvas = document.getElementById("output");
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = canvas.scrollWidth * devicePixelRatio;
canvas.height = canvas.scrollHeight * devicePixelRatio;
window.addEventListener("resize", () => {
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = canvas.scrollWidth * devicePixelRatio;
canvas.height = canvas.scrollHeight * devicePixelRatio;
});
ImGui.CreateContext();
ImGui_Impl.Init(canvas);
ImGui.StyleColorsDark();
const clear_color = new ImGui.ImVec4(0.3, 0.3, 0.3, 1.00);
const renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.gammaFactor = 2.2;
renderer.physicallyCorrectLights = true;
renderer.outputEncoding = THREE.sRGBEncoding;
const scene = new THREE.Scene();
let floorDim = 1000;
var grid = new THREE.GridHelper(floorDim, 200, 'orange', 'white');
grid.name = "grid";
grid.material.opacity = 1.0;
grid.material.transparent = true;
grid.material.color.convertGammaToLinear(2.2);
grid.position.set(0, 2, 0);
scene.add(grid);
var subgrid = new THREE.GridHelper(floorDim, 600, 'grey', 'grey');
subgrid.name = "subgrid";
subgrid.material.opacity = 1.0;
subgrid.material.color.convertGammaToLinear(2.2);
subgrid.material.transparent = true;
subgrid.position.set(0, 1, 0);
scene.add(subgrid);
const light = new THREE.DirectionalLight(0xffffff, 0.8);
light.position.set(0, 100, -50);
light.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(light);
const box_1_mesh = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 5), new THREE.MeshLambertMaterial({ color:0x970000 }));
box_1_mesh.position.set(0, 5, 0);
scene.add(box_1_mesh);
const box_2_mesh = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 5), new THREE.MeshLambertMaterial({ color:0x333000 }));
box_2_mesh.position.set(-2, 6, 7);
scene.add(box_2_mesh);
const camera = new THREE.PerspectiveCamera(50, canvas.width / canvas.height, 2, 10000);
camera.position.set(0, 10, -25);
camera.updateProjectionMatrix();
camera.lookAt(0, 0, 0);
scene.add(camera);
window.requestAnimationFrame(_loop);
function _loop(time) {
ImGui_Impl.NewFrame(time);
ImGui.NewFrame();
ImGui.SetNextWindowPos(new ImGui.ImVec2(20, 20), ImGui.Cond.FirstUseEver);
ImGui.SetNextWindowSize(new ImGui.ImVec2(294, 140), ImGui.Cond.FirstUseEver);
ImGui.Begin("Visblility Toggles:");
ImGui.Checkbox("box_1 (near)", (value = box_1_mesh.visible) => box_1_mesh.visible = value);
ImGui.Checkbox("box_2 (distant)", (value = box_2_mesh.visible) => box_2_mesh.visible = value);
ImGui.Checkbox("grid", (value = grid.visible) => grid.visible = value);
ImGui.Checkbox("subgrid", (value = subgrid.visible) => subgrid.visible = value);
ImGui.End();
ImGui.EndFrame();
ImGui.Render();
renderer.setClearColor(new THREE.Color(clear_color.x, clear_color.y, clear_color.z), clear_color.w);
renderer.setSize(canvas.width, canvas.height);
camera.aspect = canvas.width / canvas.height;
camera.updateProjectionMatrix();
renderer.render(scene, camera);
ImGui_Impl.RenderDrawData(ImGui.GetDrawData());
renderer.state.reset();
window.requestAnimationFrame(_loop);
}
})();
#output {
position: absolute;
top: 0px;
right: 0px;
width: 100%;
height: 100%;
z-index: 1;
}
<script>
// emu localStorage otherwise ImGUI fails because S.O blocks localStorage
{
const localStorageStorage = new Map();
const localStorage = {
getItem(k) { return localStorageStorage.get(k); },
setItem(k,v) { return localStorageStorage.set(k, v); },
};
Object.defineProperty(window, 'localStorage', {
get() { return localStorage; }
});
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r125/three.min.js"></script>
<script src="https://flyover.github.io/imgui-js/dist/imgui.umd.js"></script>
<script src="https://flyover.github.io/imgui-js/dist/imgui_impl.umd.js"></script>
<script src="https://flyover.github.io/nanovg-js/dist/nanovg.umd.js"></script>
<canvas tabindex="0" id="output"></canvas>
Is there anything wrong with my setup of imgui-js or three-js? Any ideas as to what I might do so that the ImGui_Impl.RenderDrawData call does not impact the rendering of my three.js scene?
The issue is imgui-js is trashing the attribute state.
You might want to consider running imgui-js in another canvas overlayed on top of the
three.js canvas, each with their own WebGL context. Then they don't have to worry about each other.
A quick hack is this
const gl = renderer.getContext();
window.vao = window.vao || gl.createVertexArray();
const oldVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
gl.bindVertexArray(vao);
ImGui_Impl.RenderDrawData(ImGui.GetDrawData());
gl.bindVertexArray(oldVao);
But that hack only works in WebGL2. To fully save and restore the missing state is not hard but handling WebGL1, and extensions, and WebGL2 is probably 50-80 lines of code.
I filed an issue here with an example PR
(async function() {
await ImGui.default();
const canvas = document.getElementById("output");
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = canvas.scrollWidth * devicePixelRatio;
canvas.height = canvas.scrollHeight * devicePixelRatio;
window.addEventListener("resize", () => {
const devicePixelRatio = window.devicePixelRatio || 1;
canvas.width = canvas.scrollWidth * devicePixelRatio;
canvas.height = canvas.scrollHeight * devicePixelRatio;
});
ImGui.CreateContext();
ImGui_Impl.Init(canvas);
ImGui.StyleColorsDark();
const clear_color = new ImGui.ImVec4(0.3, 0.3, 0.3, 1.00);
const renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.gammaFactor = 2.2;
renderer.physicallyCorrectLights = true;
renderer.outputEncoding = THREE.sRGBEncoding;
const scene = new THREE.Scene();
let floorDim = 1000;
var grid = new THREE.GridHelper(floorDim, 200, 'orange', 'white');
grid.name = "grid";
grid.material.opacity = 1.0;
grid.material.transparent = true;
grid.material.color.convertGammaToLinear(2.2);
grid.position.set(0, 2, 0);
scene.add(grid);
var subgrid = new THREE.GridHelper(floorDim, 600, 'grey', 'grey');
subgrid.name = "subgrid";
subgrid.material.opacity = 1.0;
subgrid.material.color.convertGammaToLinear(2.2);
subgrid.material.transparent = true;
subgrid.position.set(0, 1, 0);
scene.add(subgrid);
const light = new THREE.DirectionalLight(0xffffff, 0.8);
light.position.set(0, 100, -50);
light.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(light);
const box_1_mesh = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 5), new THREE.MeshLambertMaterial({ color:0x970000 }));
box_1_mesh.position.set(0, 5, 0);
scene.add(box_1_mesh);
const box_2_mesh = new THREE.Mesh(new THREE.BoxGeometry(5, 5, 5), new THREE.MeshLambertMaterial({ color:0x333000 }));
box_2_mesh.position.set(-2, 6, 7);
scene.add(box_2_mesh);
const camera = new THREE.PerspectiveCamera(50, canvas.width / canvas.height, 2, 10000);
camera.position.set(0, 10, -25);
camera.updateProjectionMatrix();
camera.lookAt(0, 0, 0);
scene.add(camera);
setTimeout(() => {
grid.visible = false;
}, 1000);
setTimeout(() => {
grid.visible = true;
}, 2000);
setTimeout(() => {
console.log(grid.visible);
}, 2500);
window.requestAnimationFrame(_loop);
function _loop(time) {
ImGui_Impl.NewFrame(time);
ImGui.NewFrame();
ImGui.SetNextWindowPos(new ImGui.ImVec2(20, 20), ImGui.Cond.FirstUseEver);
ImGui.SetNextWindowSize(new ImGui.ImVec2(294, 140), ImGui.Cond.FirstUseEver);
ImGui.Begin("Visblility Toggles:");
ImGui.Checkbox("box_1 (near)", (value = box_1_mesh.visible) => box_1_mesh.visible = value);
ImGui.Checkbox("box_2 (distant)", (value = box_2_mesh.visible) => box_2_mesh.visible = value);
ImGui.Checkbox("grid", (value = grid.visible) => grid.visible = value);
ImGui.Checkbox("subgrid", (value = subgrid.visible) => subgrid.visible = value);
ImGui.End();
ImGui.EndFrame();
ImGui.Render();
renderer.setClearColor(new THREE.Color(clear_color.x, clear_color.y, clear_color.z), clear_color.w);
renderer.setSize(canvas.width, canvas.height);
camera.aspect = canvas.width / canvas.height;
camera.updateProjectionMatrix();
renderer.render(scene, camera);
const gl = renderer.getContext();
window.vao = window.vao || gl.createVertexArray();
const oldVao = gl.getParameter(gl.VERTEX_ARRAY_BINDING);
gl.bindVertexArray(vao);
ImGui_Impl.RenderDrawData(ImGui.GetDrawData());
gl.bindVertexArray(oldVao);
renderer.state.reset();
window.requestAnimationFrame(_loop);
}
})();
#output {
position: absolute;
top: 0px;
right: 0px;
width: 100%;
height: 100%;
z-index: 1;
}
<script>
// emu localStorage otherwise ImGUI fails because S.O blocks localStorage
{
const localStorageStorage = new Map();
const localStorage = {
getItem(k) { return localStorageStorage.get(k); },
setItem(k,v) { return localStorageStorage.set(k, v); },
};
Object.defineProperty(window, 'localStorage', {
get() { return localStorage; }
});
}
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r125/three.js"></script>
<script src="https://flyover.github.io/imgui-js/dist/imgui.umd.js"></script>
<script src="https://flyover.github.io/imgui-js/dist/imgui_impl.umd.js"></script>
<script src="https://flyover.github.io/nanovg-js/dist/nanovg.umd.js"></script>
<canvas tabindex="0" id="output"></canvas>
I'm creating a A-Frame Application (with Three.js) draw a road using THREE.CatmullRomCurve3.
The application's concept is draw a road which connects multi points, I want to draw the road with exact width (eg: a road with 3 meters width), I'm using THREE.PlaneGeometry to connect 2 points.
My result, and my code snippet
var scene, camera, renderer;
var cube;
var controls;
function initScene() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 30;
renderer = new THREE.WebGLRenderer({
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
controls = new THREE.OrbitControls(camera);
controls.update();
document.body.appendChild(renderer.domElement);
}
function render() {
requestAnimationFrame(render);
// required if controls.enableDamping or controls.autoRotate are set to true
controls.update();
renderer.render(scene, camera);
}
function drawRoadByLine() {
//Create a closed wavey loop
var curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-10, 0, 10),
new THREE.Vector3(-5, 5, 5),
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(5, -5, 5),
new THREE.Vector3(10, 0, 10)
]);
var points = curve.getPoints(50);
var geometry = new THREE.BufferGeometry().setFromPoints(points);
var material = new THREE.LineBasicMaterial({
color: 0xff0000
});
// Create the final object to add to the scene
var curveObject = new THREE.Line(geometry, material);
scene.add(curveObject);
}
function drawRoadByPlane() {
//Create a closed wavey loop
var curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-10, 0, 10),
new THREE.Vector3(-5, 5, 5),
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(5, -5, 5),
new THREE.Vector3(10, 0, 10)
]);
var points = curve.getPoints(50);
var group = new THREE.Group();
var currentPos;
var nextPos;
var distance;
var plane;
var rotationMatrix;
for (var i = 0; i < points.length - 1; i++) {
currentPos = new THREE.Vector3(points[i].x, points[i].y, points[i].z);
nextPos = new THREE.Vector3(points[i + 1].x, points[i + 1].y, points[i + 1].z);
distance = currentPos.distanceTo(nextPos);
plane = createPlane(distance);
plane.position.set(currentPos.x, currentPos.y, currentPos.z);
// rotationMatrix = getRotationMatrix(currentPos, nextPos);
// plane.applyMatrix(rotationMatrix);
group.add(plane);
}
scene.add(group);
}
function createPlane(distance, position) {
var geometry = new THREE.PlaneGeometry(1, distance);
// Dummy random color each plane, true color is red (0xff0000)
var color = Math.floor((Math.random() * 0xffffff) + 1);
var material = new THREE.MeshBasicMaterial({
color: color,
side: THREE.DoubleSide
});
var plane = new THREE.Mesh(geometry, material);
return plane;
}
function getRotationMatrix(v1, v2) {
var quaternion = new THREE.Quaternion();
quaternion.setFromUnitVectors(v1, v2);
var matrix = new THREE.Matrix4();
matrix.makeRotationFromQuaternion(quaternion);
return matrix;
}
initScene();
drawRoadByPlane();
render();
body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #000000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
My now issue is how to make the planes display continously (without space between 2 plane). I think I need rotate each plane, but I don't know to to calculate right rotation between 2 points.
Update:
I've just changed my solution to use THREE.Face3 instead of THREE.PlaneGeometry.
Here is my code snippet
var scene, camera, renderer;
var cube;
var controls;
function initScene() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 30;
renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.update();
}
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function draw() {
var dummyPoints = [
new THREE.Vector3(-10, 0, 10),
new THREE.Vector3(-5, 5, 5),
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(5, -5, 5),
new THREE.Vector3(10, 0, 10)
];
//Create a closed wavey loop
var curve = new THREE.CatmullRomCurve3(dummyPoints);
var material = new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors, side: THREE.DoubleSide });
//create a triangular geometry
var points = curve.getPoints(100);
var roadPoints = [];
var length = points.length;
for (var i = 0; i < length - 1; i++) {
roadPoints = roadPoints.concat(extractRoadPoint(points[i], points[i + 1]));
}
roadPoints = roadPoints.concat(extractRoadPoint(points[length - 1], points[length - 2]));
var geometry = new THREE.Geometry().setFromPoints(roadPoints);
// var face = new THREE.Face3(0, 1, 2);
//add the face to the geometry's faces array
// geometry.faces.push(face);
for (var i = 0; i < roadPoints.length - 2; i++) {
var face = new THREE.Face3(i, i + 1, i + 2);
geometry.faces.push(face);
face.color.set(new THREE.Color(Math.random() * 0xffffff - 1));
}
//the face normals and vertex normals can be calculated automatically if not supplied above
geometry.computeFaceNormals();
geometry.computeVertexNormals();
scene.add(new THREE.Mesh(geometry, material));
}
function extractRoadPoint(point1, point2) {
var result = [];
var vector = {
x: point2.x - point1.x,
y: point2.y - point1.y,
z: point2.z - point1.z,
}
var uOxz = {
x: 0,
y: 1,
z: 0
};
var vectorVertices = {
x: vector.y * uOxz.z - vector.z * uOxz.y,
y: vector.z * uOxz.x - vector.x * uOxz.z,
z: vector.x * uOxz.y - vector.y * uOxz.x,
};
var t = Math.sqrt(1 * 1 / (vectorVertices.x * vectorVertices.x + vectorVertices.y * vectorVertices.y + vectorVertices.z * vectorVertices.z));
var sidePoint11 = {
x: point1.x + vectorVertices.x * t,
y: point1.y + vectorVertices.y * t,
z: point1.z + vectorVertices.z * t,
}
var sidePoint12 = {
x: point1.x - vectorVertices.x * t,
y: point1.y - vectorVertices.y * t,
z: point1.z - vectorVertices.z * t,
}
var sidePoint21 = {
x: point2.x + vectorVertices.x * t,
y: point2.y + vectorVertices.y * t,
z: point2.z + vectorVertices.z * t,
}
var sidePoint22 = {
x: point2.x - vectorVertices.x * t,
y: point2.y - vectorVertices.y * t,
z: point2.z - vectorVertices.z * t,
}
return [sidePoint11, sidePoint12, sidePoint21, sidePoint22];
}
initScene();
draw();
render();
body {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #000000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
Following is my code, of the ThreeJS scene, I've checked and rechecked it several and there is no syntax error as well. But no luck so far. Sometimes it does give few Runtime errors in the imported script namely "Unable to get property 'center' of undefined or null reference"
Please Help help me out as I've to prepare this project by next week.
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/build/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/controls/TrackballControls.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/renderers/CSS3DRenderer.js"></script>
<script>
var controls, camera, scene, cssScene, glRenderer, cssRenderer;
function createGLRenderer()
{
var glRenderer = new THREE.WebGLRenderer({ alpha:true, antialias:true });
glRenderer.setClearColor("rgb(255, 255, 230)");
glRenderer.setPixelRatio(window.devicePixelRatio);
glRenderer.setSize(window.innerWidth, window.innerHeight);
glRenderer.domElement.style.position = 'absolute';
glRenderer.domElement.style.zIndex = 1;
glRenderer.domElement.style.top = 0;
return glRenderer;
}
function createCSSRenderer()
{
var cssRenderer = new THREE.CSS3DRenderer();
cssRenderer.setSize(window.innerWidth, window.innerHeight);
cssRenderer.domElement.style.position = 'absolute';
glRenderer.domElement.style.zIndex = 0;
cssRenderer.domElement.style.top = 0;
return cssRenderer;
}
function createPlane(w, h, position, rotation)
{
var material1 = new THREE.MeshBasicMaterial({ color: "rgb(255, 0, 0)", opacity: 0.0, side: THREE.DoubleSide });
var geometry1 = new THREE.PlaneGeometry(w, h);
var mesh1 = new THREE.Mesh(material1, geometry1);
mesh1.position.x = position.x;
mesh1.position.y = position.y;
mesh1.position.z = position.z;
mesh1.rotation.x = rotation.x;
mesh1.rotation.y = rotation.y;
mesh1.rotation.z = rotation.z;
return mesh1;
}
function createCssObject(w, h, position, rotation, url)
{
var html = [
'<div style = "width:' + w + 'px; height:' + h + 'px;">',
'<iframe src = "' + url + '" width = "' + w + '" height = "' + h + '">',
'</iframe>',
'</div>'
].join('\n');
var div = document.createElement('div');
$(div).html(html);
var CssObject = new THREE.CSS3DObject(div);
CssObject.position.x = position.x;
CssObject.position.y = position.y;
CssObject.position.z = position.z;
CssObject.rotation.x = rotation.x;
CssObject.rotation.y = rotation.y;
CssObject.rotation.z = rotation.z;
return CssObject;
}
function create3Dpage(w, h, position, rotation, url)
{
var plane = createPlane(w, h, position, rotation);
scene.add(plane);
var cssObject = createCssObject(w, h, position, rotation, url);
cssScene.add(cssObject);
}
function initialize()
{
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.set(0, 100, 3000);
//controls = new THREE.TrackballControls(camera);
glRenderer = createGLRenderer();
cssRenderer = createCSSRenderer();
document.body.appendChild(cssRenderer.domElement);
cssRenderer.domElement.appendChild(glRenderer.domElement);
scene = new THREE.Scene();
cssScene = new THREE.Scene();
var alight = new THREE.AmbientLight("rgb(255, 255, 255)",0.5);
scene.add(alight);
create3Dpage(1000, 1000, new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 0), 'http://adndevblog.typepad.com/cloud_and_mobile');
Render();
}
function Render()
{
//controls.update();
glRenderer.render(scene, camera);
cssRenderer.render(cssScene, camera);
requestAnimationFrame(Render);
}
$(document ).ready(function() {
initialize();
});
</script>