I'm making a 2d game, where blocks are falling down ( tetris style). I need to render alphabets on these blocks. This is how I am creating blocks:
var geometry = new THREE.BoxGeometry( this.BLOCK_WIDTH, this.BLOCK_WIDTH, 4 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
this.blocks = [];
for (var i = 0; i < rows * columns; i++) {
cube = new THREE.Mesh( geometry, material );
cube.visible = false;
cube.letter = letterGenerator.getNextLetter();
this.blocks[i] = cube;
scene.add( this.blocks[i] );
};
As you can see, all blocks will look exactly alike except for the fact, that they will have a different alphabet associated with them. In my update(), I move the block, left/right or down. When I do so, block position will be updated and obviously the alphabet should be rendered accordingly.
How should I go about rendering alphabets on these blocks ?
EDIT: I am using WebGLRenderer.
You can get the screen position of each block (your "cube" variable above) that you want to paint text on and use HTML to paint text at that screen location over each block. Using HTML to make a text sprite like this is discussed here:
https://github.com/mrdoob/three.js/issues/1321
You can get the screen position for your "cube" above like so:
var container = document.getElementById("idcanvas");
var containerWidth = container.clientWidth;
var containerHeight = container.clientHeight;
var widthHalf = containerWidth / 2, heightHalf = containerHeight / 2;
var locvector = new THREE.Vector3();
locvector.setFromMatrixPosition(cube.matrixWorld);
locvector.project(your_camera); //returns center of mesh
var xpos = locvector.x = (locvector.x * widthHalf) + widthHalf; //convert to screen coordinates
var ypos = locvector.y = -(locvector.y * heightHalf) + heightHalf;
You'll have to update the HTML for cube movement.
Another approach is to create specific textures with the text you want and apply each texture as a material to the appropriate cube.
Related
I am trying to create a 3 JS extension for Thingworx, but the renderHtml keeps bugging in combination with a 3 JS canvas in it (See code).
//runtime.ts file
renderHtml(): string {
let htmlString = '<div class="widget-content"><canvas></canvas></div>';
return htmlString;
}
afterRender(): void {
const OrbitControls = require('three-orbit-controls')(CourseView);
const OBJLoader = require('three-obj-loader')(CourseView);
var scene = new CourseView.Scene();
var width = this.getProperty('SceneWidth', 0);
var height = this.getProperty('SceneHeight', 0);
var color = this.getProperty('SceneColor', '#000000');
if(width <= 0) { width = window.innerWidth }
if(height <= 0) { height = window.innerHeight }
if(color == undefined){ color = "#000000" }
var ratio = width / height;
var camera = new CourseView.PerspectiveCamera(75, ratio, 0.1, 1000);
camera.position.z = 30;
var cv = this.jqElement.find("canvas").get(0);
console.log(cv);
this.renderer = new CourseView.WebGLRenderer({canvas: cv});
this.renderer.setSize(width, height);
this.renderer.setClearColor("#0000ff");
var control = new OrbitControls(camera, this.renderer.domElement);
const geometry = new CourseView.SphereGeometry( 15, 32, 16 );
const material = new CourseView.MeshBasicMaterial( { color: 0xff00ff, wireframe: true } );
const sphere = new CourseView.Mesh( geometry, material );
scene.add( sphere );
control.addEventListener('change', () => this.myRender(scene, camera));
this.myRender(scene, camera);
}
myRender(scene, camera) {
this.renderer.render(scene, camera);
}
As shown, the WebGLRenderer gets the canvas inside the div with the class widget-content. I need this div, to realize bindings of Thingworks. When I leave out the div, everything works fine. If the div exists to implement bindings, the sphere is not rendered. Moreover, the renderer seems stuck and also has no blue background, despite the clear-color call.
When I click on it (maybe its not updated) the color changes to blue, but still there is no sphere. Does anyone has realized ThreeJS in Thingworx and can show me how they did it? I think maybe the div widget-content does apply some changes to all childern (also my ThreeJS canvas), but I cant tell which changes... Maybe someone knows?
Full code: https://www.toptal.com/developers/hastebin/olelowawih.js
For those of you might having this problem in the future, check your setter, you might want to render there as well and keep track of NaN values...
What I have:
var pointA = new THREE.Vector3(camera_RC_Holder.position.x, camera_RC_Holder.position.y, camera_RC_Holder.position.z);
var direction = camera_RC.position.clone();
direction.applyMatrix4( camera_RC.matrixWorld );
direction.normalize();
var distance = 700;
var pointB = new THREE.Vector3();
pointB.addVectors ( pointA, direction.multiplyScalar( -distance ) );
var geometry = new THREE.Geometry();
geometry.vertices.push( pointA );
geometry.vertices.push( pointB );
var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
var line = new THREE.Line( geometry, material );
scene_Main.add( line );
What I want:
What I'm trying to do is to show that a ray has began from the camera and explores through the view volume. So, instead of instantly create a line (point_A, point_B) I want to grow the line from point_A pixel by pixel until it meets it's destination (point_B).
Question:
How to draw the lines pixel by pixel as shown in the code snippet below??
var w = 200;
var h = 150;
var x;
function setup(){
createCanvas(w,h);
x=0;
y=0;
}
function draw(){
if(x>w){
x = 0;
}
background(250);
line(0,50,x,50); //x1,y1,x2,y2
x++;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>
Title question and explanation does not fit to me very well, maybe I did not understand your question perfectly. I will try to answer just the title question and edit this answer as soon as some clarifications are done from your side.
( Also I would always try to avoid multi-questions as much as possible. )
About the title question:
Adding something to the scene is basically drawing it... or do you mean something else?
Bind this code snippet to your click mouse event. This will create your raycast, in order to see it you need to add it to your scene. Afterwards you can move your camera and check how it looks like:
var material = new THREE.LineBasicMaterial({
color: 0x0000ff
});
var geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(raycaster.ray.origin.x, raycaster.ray.origin.y, raycaster.ray.origin.z));
geometry.vertices.push(new THREE.Vector3(raycaster.ray.origin.x + (raycaster.ray.direction.x * 100000), raycaster.ray.origin.y + (raycaster.ray.direction.y * 100000), raycaster.ray.origin.z + (raycaster.ray.direction.z * 100000)));
var line = new THREE.Line(geometry, material);
I am currently trying to create some smooth terrain using the PlaneBufferGeometry of three.js from a height map I got from Google Images:
https://forums.unrealengine.com/filedata/fetch?id=1192062&d=1471726925
but the result is kinda choppy..
(Sorry, this is my first question and evidently I need 10 reputation to post images, otherwise I would.. but here's an even better thing: a live demo! left click + drag to rotate, scroll to zoom)
I want, like i said, a smooth terrain, so am I doing something wrong or is this just the result and i need to smoothen it afterwards somehow?
Also here is my code:
const IMAGE_SRC = 'terrain2.png';
const SIZE_AMPLIFIER = 5;
const HEIGHT_AMPLIFIER = 10;
var WIDTH;
var HEIGHT;
var container = jQuery('#wrapper');
var scene, camera, renderer, controls;
var data, plane;
image();
// init();
function image() {
var image = new Image();
image.src = IMAGE_SRC;
image.onload = function() {
WIDTH = image.width;
HEIGHT = image.height;
var canvas = document.createElement('canvas');
canvas.width = WIDTH;
canvas.height = HEIGHT;
var context = canvas.getContext('2d');
console.log('image loaded');
context.drawImage(image, 0, 0);
data = context.getImageData(0, 0, WIDTH, HEIGHT).data;
console.log(data);
init();
}
}
function init() {
// initialize camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, .1, 100000);
camera.position.set(0, 1000, 0);
// initialize scene
scene = new THREE.Scene();
// initialize directional light (sun)
var sun = new THREE.DirectionalLight(0xFFFFFF, 1.0);
sun.position.set(300, 400, 300);
sun.distance = 1000;
scene.add(sun);
var frame = new THREE.SpotLightHelper(sun);
scene.add(frame);
// initialize renderer
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.append(renderer.domElement);
// initialize controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = .05;
controls.rotateSpeed = .1;
// initialize plane
plane = new THREE.PlaneBufferGeometry(WIDTH * SIZE_AMPLIFIER, HEIGHT * SIZE_AMPLIFIER, WIDTH - 1, HEIGHT - 1);
plane.castShadow = true;
plane.receiveShadow = true;
var vertices = plane.attributes.position.array;
// apply height map to vertices of plane
for(i=0, j=2; i < data.length; i += 4, j += 3) {
vertices[j] = data[i] * HEIGHT_AMPLIFIER;
}
var material = new THREE.MeshPhongMaterial({color: 0xFFFFFF, side: THREE.DoubleSide, shading: THREE.FlatShading});
var mesh = new THREE.Mesh(plane, material);
mesh.rotation.x = - Math.PI / 2;
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
plane.computeFaceNormals();
plane.computeVertexNormals();
scene.add(mesh);
animate();
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
controls.update();
}
The result is jagged because the height map has low color depth. I took the liberty of coloring a portion of the height map (Paint bucket in Photoshop, 0 tolerance, non-continuous) so you can see for yourself how large are the areas which have the same color value, i.e. the same height.
The areas of the same color will create a plateau in your terrain. That's why you have plateaus and sharp steps in your terrain.
What you can do is either smooth out the Z values of the geometry or use a height map which utilizes 16bits or event 32bits for height information. The current height map only uses 8bits, i.e. 256 values.
One thing you could do to smooth things out a bit is to sample more than just a single pixel from the heightmap. Right now, the vertex indices directly correspond to the pixel position in the data-array. And you just update the z-value from the image.
for(i=0, j=2; i < data.length; i += 4, j += 3) {
vertices[j] = data[i] * HEIGHT_AMPLIFIER;
}
Instead you could do things like this:
get multiple samples with certain offsets along the x/y axes
compute an (weighted) average value from the samples
That way you would get some smoothing at the borders of the same-height areas.
The second option is to use something like a blur-kernel (gaussian blur is horribly expensive, but maybe something like a fast box-blur would work for you).
As you are very limited in resolution due to just using a single byte, you should convert that image to float32 first:
const highResData = new Float32Array(data.length / 4);
for (let i = 0; i < highResData.length; i++) {
highResData[i] = data[4 * i] / 255;
}
Now the data is in a format that allows for far higher numeric resolution, so we can smooth that now. You could either adjust something like the StackBlur for the float32 use-case, use ndarrays and ndarray-gaussian-filter or implement something simple yourself. The basic idea is to find an average value for all the values in those uniformly colored plateaus.
Hope that helps, good luck :)
My display has a resolution of 7680x4320 pixels. I want to display up to 4 million different colored squares. And I want to change the number of squares with a slider. If have currently two versions. One with canvas-fillRect which looks somethink like this:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
for (var i = 0; i < num_squares; i ++) {
ctx.fillStyle = someColor;
ctx.fillRect(pos_x, pos_y, pos_x + square_width, pos_y + square_height);
// set pos_x and pos_y for next square
}
And one with webGL and three.js. Same loop, but I create a box geometry and a mesh for every square:
var geometry = new THREE.BoxGeometry( width_height, width_height, 0);
for (var i = 0; i < num_squares; i ++) {
var material = new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } );
material.emissive = new THREE.Color( Math.random(), Math.random(), Math.random() );
var object = new THREE.Mesh( geometry, material );
}
They both work quite fine for a few thousand squares. The first version can do up to one million squares, but everything over a million is just awful slow. I want to update the color and the number of squares dynamically.
Does anyone has tips on how to be more efficient with three.js/ WebGL/ Canvas?
EDIT1: Second version: This is what I do at the beginning and when the slider has changed:
// Remove all objects from scene
var obj, i;
for ( i = scene.children.length - 1; i >= 0 ; i -- ) {
obj = scene.children[ i ];
if ( obj !== camera) {
scene.remove(obj);
}
}
// Fill scene with new objects
num_squares = gui_dat.squareNum;
var window_pixel = window.innerWidth * window.innerHeight;
var pixel_per_square = window_pixel / num_squares;
var width_height = Math.floor(Math.sqrt(pixel_per_square));
var geometry = new THREE.BoxGeometry( width_height, width_height, 0);
var pos_x = width_height/2;
var pos_y = width_height/2;
for (var i = 0; i < num_squares; i ++) {
//var object = new THREE.Mesh( geometry, );
var material = new THREE.Material()( { color: Math.random() * 0xffffff } );
material.emissive = new THREE.Color( Math.random(), Math.random(), Math.random() );
var object = new THREE.Mesh( geometry, material );
object.position.x = pos_x;
object.position.y = pos_y;
pos_x += width_height;
if (pos_x > window.innerWidth) {
pos_x = width_height/2;
pos_y += width_height;
}
scene.add( object );
}
The fastest way to draw squares is to use the gl.POINTS primitive and then setting gl_PointSize to the pixel size.
In three.js, gl.POINTS is wrapped inside the THREE.PointCloud object.
You'll have to create a geometry object with one position for each point and pass that to the PointCloud constructor.
Here is an example of THREE.PointCloud in action:
http://codepen.io/seanseansean/pen/EaBZEY
geometry = new THREE.Geometry();
for (i = 0; i < particleCount; i++) {
var vertex = new THREE.Vector3();
vertex.x = Math.random() * 2000 - 1000;
vertex.y = Math.random() * 2000 - 1000;
vertex.z = Math.random() * 2000 - 1000;
geometry.vertices.push(vertex);
}
...
materials[i] = new THREE.PointCloudMaterial({size:size});
particles = new THREE.PointCloud(geometry, materials[i]);
I didn't dig through all the code but I've set the particle count to 2m and from my understanding, 5 point clouds are generated so 2m*5 = 10m particles and I'm getting around 30fps.
The highest number of individual points I've seen so far was with potree.
http://potree.org/, https://github.com/potree
Try some demo, I was able to observe 5 millions of points in 3D at 20-30fps. I believe this is also current technological limit.
I didn't test potree on my own, so I cant say much about this tech. But there is data convertor and viewer (threejs based) so should only figure out how to convert the data.
Briefly about your question
The best way handle large data is group them as quad-tree (2d) or oct-tree (3d). This will allow you to not bother program with part that is too far from camera or not visible at all.
On the other hand, program doesnt like when you do too many webgl calls. Try to understand it like this, you want to do create ~60 images each second. But each time you set some parameter for GPU, program must do some sync. Spliting data means you will need to do more setup so tree must not be too detialed.
Last thing, someone said:
You'll probably want to pass an array of values as one of the shader uniforms
I dont suggest it, bad idea. Texture lookup is quite fast, but attributes are always faster. If we are talking about 4M points, you cant afford reading data from uniforms.
Sorry I cant help you with the code, I could do it without threejs, Im not threejs expert :)
I would recommend trying pixi framework( as mentioned in above comments ).
It has webgl renderer and some benchmarks are very promising.
http://www.goodboydigital.com/pixijs/bunnymark_v3/
It can handle allot of animated sprites.
If your app only displays the squares, and doesnt animate, and they are very simple sprites( only one color ) then it would give better performance than the demo link above.
I am creating a dynamic cube that can be dynamically changed by scaling its mesh. The issue is, I would like to keep it fixed to the floor when modifying its height. This is a snippet of my code:
function init() {
// Floor position
floor = new THREE.Mesh( shadowGeo, shadowMaterial );
floor.position.y = 0;
floor.rotation.x = - Math.PI / 2;
scene.add( floor );
// Defines the cube and its original position
var BoxGeometry = new THREE.BoxGeometry(50, 50, 50);
var boxMaterial = new THREE.MeshLambertMaterial({color: 0x000088});
cube = new THREE.Mesh(BoxGeometry, boxMaterial);
cube.position.set(0,30,0);
scene.add(cube);
// GUI PANEL INTERACTION
// Now the GUI panel for the interaction is defined
gui = new dat.GUI();
parameters = {
height: 1,
reset: function() {resetCube()}
}
// Define the second folder which takes care of the scaling of the cube
var folder1 = gui.addFolder("Dimensions");
var cubeHeight = folder2.add(parameters, "height").min(0).max(200).step(1);
folder1.open();
// Function taking care of the cube changes
cubeHeight.onChange(function(value){cube.scale.y = value;});
gui.open();
}
// Update cube characteristics
function updateCube() {
cube.scale.y = parameters.y;
}
// Reset cube settings
function resetCube() {
parameters.height = 1;
updateCube();
}
// Rest of the code
I have searched around and I saw this similar topic, but still it does not properly explain how to modify dimensions when the object with a reference floor. Do you know how can I solve this issue?
Changed your .onChange() function to have the cube stay on the ground:
// Function taking care of the cube changes
cubeHeightScale.onChange(
function(value)
{
cube.scale.y = value;
cube.position.y = (cubeHeight * value) / 2;
} );
Here is a fiddle to check the changes live: http://jsfiddle.net/Lsjh965o/
three.js r71