Related
I have been trying to create a full circle using the triangle fan approach. However, I've tried increasing the number of fans from 80 to 360. Then I tried increasing it to 500, 5000, 50000. It disappears at 50000 only because the slice is so small... I am wondering how I can fill in that missing slice.
Here is the code I am working with:
// RotatingTriangle.js (c) 2012 matsuda
// Vertex shader program
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'uniform mat4 u_ModelMatrix;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' +
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
'}\n';
function main() {
// Retrieve <canvas> element
var canvas = document.getElementById('webgl');
// Get the rendering context for WebGL
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// Initialize shaders
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// Write the positions of vertices to a vertex shader
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}
// Specify the color for clearing <canvas>
gl.clearColor(0, 0, 0, 1);
// Clear <canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw the rectangle
gl.drawArrays(gl.TRIANGLE_FAN, 0, n);
}
function initVertexBuffers(gl) {
var circle = {x: 0, y:0, r: 0.75};
var ATTRIBUTES = 2;
var numFans = 64;
var degreePerFan = (2* Math.PI) / numFans;
var vertexData = [
0.0, 0.0
];
// updated here, but the problem still persists
for(var i = 0; i <= numFans; i++) {
var index = 2 + i*2;
var angle = degreePerFan * (i+1);
//console.log(angle)
vertexData[index] = Math.cos(angle) * 0.5;
vertexData[index + 1] = Math.sin(angle) * 0.5;
}
//console.log(vertexData);
var vertexDataTyped = new Float32Array(vertexData);
// Create a buffer object
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
// Bind the buffer object to target
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// Write date into the buffer object
gl.bufferData(gl.ARRAY_BUFFER, vertexDataTyped, gl.STATIC_DRAW);
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
// Assign the buffer object to a_Position variable
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// Enable the assignment to a_Position variable
gl.enableVertexAttribArray(a_Position);
return numFans;
}
Picture of missing slice
// SOLUTION
for(var i = 0; i <= numFans; i++) {
var index = i*2; <--- (used to be 'var index = 2 + i*2;')
var angle = degreePerFan * (i+1);
//console.log(angle)
vertexData[index] = Math.cos(angle) * 0.5;
vertexData[index + 1] = Math.sin(angle) * 0.5;
}
I cannot reproduce the issue, however the generation of the vertices is not correct. The index of the 2nd vertex coordinate is 0.0, 0.0:
var index = 2*3 + i*2;
var index = 2 + i*2;
Vertex generation:
var vertexData = [
0.0, 0.0,
];
for(var i = 0; i <= numFans; i++) {
var index = 2 + i*2;
var angle = degreePerFan * i;
vertexData[index] = Math.cos(angle) * 0.5;
vertexData[index + 1] = Math.sin(angle) * 0.5;
}
I want to move a object (circle in this case) through array of coordinates (for example: {(300,400), (200,300), (300,200),(400,400)})on HTML5 Canvas. I could move the object to one coordinate as follows. The following code draws a circle at (100,100) and moves it to (300,400). I am stuck when trying to extend this so that circle moves through set of coordinates one after the other.
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
//circle object
let circle ={
x:100,
y:100,
radius:10,
dx:1,
dy:1,
color:'blue'
}
//function to draw above circle on canvas
function drawCircle(){
ctx.beginPath();
ctx.arc(circle.x,circle.y,circle.radius,0,Math.PI*2);
ctx.fillStyle=circle.color;
ctx.fill();
ctx.closePath();
}
//Moving to a target coordinate (targetX,targetY)
function goTo(targetX,targetY){
if(Math.abs(circel.x-targetX)<circle.dx && Math.abs(circel.y-targetY)<circle.dy){
circle.dx=0;
circle.dy=0;
circel.x = targetX;
circle.y = targetY;
}
else{
const opp = targetY - circle.y;
const adj = targetX - circle.x;
const angle = Math.atan2(opp,adj)
circel.x += Math.cos(angle)*circle.dx
circle.y += Math.sin(angle)*circle.dy
}
}
function update(){
ctx.clearRect(0,0,canvas.width,canvas.height);
drawCircle()
goTo(300,400)
requestAnimationFrame(update);
}
update()
Random access key frames
For the best control of animations you need to create way points (key frames) that can be accessed randomly by time. This means you can get any position in the animation just by setting the time.
You can then play and pause, set speed, reverse and seek to any position in the animation.
Example
The example below uses a set of points and adds data required to quickly locate the key frames at the requested time and interpolate the position.
The blue dot will move at a constant speed over the path in a time set by pathTime in this case 4 seconds.
The red dot's position is set by the slider. This is to illustrate the random access of the animation position.
const ctx = canvas.getContext('2d');
const pathTime = 4; // Total time to travel path from start to end in seconds
var startTime, animTime = 0, paused = false;
requestAnimationFrame(update);
const P2 = (x, y) => ({x, y, dx: 0,dy: 0,dist: 0, start: 0, end: 0});
const pathCoords = [
P2(20, 20), P2(100, 50),P2(180, 20), P2(150, 100), P2(180, 180),
P2(100, 150), P2(20, 180), P2(50, 100), P2(20, 20),
];
createAnimationPath(pathCoords);
const circle ={
draw(rad = 10, color = "blue") {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(this.x, this.y, rad, 0, Math.PI * 2);
ctx.fill();
}
};
function createAnimationPath(points) { // Set up path for easy random position lookup
const segment = (prev, next) => {
[prev.dx, prev.dy] = [next.x - prev.x, next.y - prev.y];
prev.dist = Math.hypot(prev.dx, prev.dy);
next.end = next.start = prev.end = prev.start + prev.dist;
}
var i = 1;
while (i < points.length) { segment(points[i - 1], points[i++]) }
}
function getPos(path, pos, res = {}) {
pos = (pos % 1) * path[path.length - 1].end; // loop & scale to total length
const pathSeg = path.find(p => pos >= p.start && pos <= p.end);
const unit = (pos - pathSeg.start) / pathSeg.dist; // unit distance on segment
res.x = pathSeg.x + pathSeg.dx * unit; // x, y position on segment
res.y = pathSeg.y + pathSeg.dy * unit;
return res;
}
function update(time){
// startTime ??= time; // Throws syntax on iOS
startTime = startTime ?? time; // Fix for above
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (paused) { startTime = time - animTime }
else { animTime = time - startTime }
getPos(pathCoords, (animTime / 1000) / pathTime, circle).draw();
getPos(pathCoords, timeSlide.value, circle).draw(5, "red");
requestAnimationFrame(update);
}
pause.addEventListener("click", ()=> { paused = true; pause.classList.add("pause") });
play.addEventListener("click", ()=> { paused = false; pause.classList.remove("pause") });
rewind.addEventListener("click", ()=> { startTime = undefined; animTime = 0 });
div {
position:absolute;
top: 5px;
left: 20px;
}
#timeSlide {width: 360px}
.pause {color:blue}
button {height: 30px}
<div><input id="timeSlide" type="range" min="0" max="1" step="0.001" value="0" width= "200"><button id="rewind">Start</button><button id="pause">Pause</button><button id="play">Play</button></div>
<canvas id="canvas" width="200" height="200"></canvas>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// array of path coords
const pathCoords = [
[200,100],
[300, 150],
[200,190],
[400,100],
[50,10],
[150,10],
[0, 50],
[500,90],
[20,190],
[10,180],
];
// current point
let currentTarget = pathCoords.shift();
//circle object
const circle ={
x:10,
y:10,
radius:10,
dx:2,
dy:2,
color:'blue'
}
//function to draw above circle on canvas
function drawCircle(){
ctx.beginPath();
ctx.arc(circle.x,circle.y,circle.radius,0,Math.PI*2);
ctx.fillStyle=circle.color;
ctx.fill();
ctx.closePath();
}
//Moving to a target coordinate (targetX,targetY)
function goTo(targetX, targetY){
if(Math.abs(circle.x-targetX)<circle.dx && Math.abs(circle.y-targetY)<circle.dy){
// dont stop...
//circle.dx = 0;
//circle.dy = 0;
circle.x = targetX;
circle.y = targetY;
// go to next point
if (pathCoords.length) {
currentTarget = pathCoords.shift();
} else {
console.log('Path end');
}
} else {
const opp = targetY - circle.y;
const adj = targetX - circle.x;
const angle = Math.atan2(opp,adj)
circle.x += Math.cos(angle)*circle.dx
circle.y += Math.sin(angle)*circle.dy
}
}
function update(){
ctx.clearRect(0,0,canvas.width,canvas.height);
drawCircle();
goTo(...currentTarget);
requestAnimationFrame(update);
}
update();
<canvas id=canvas width = 500 height = 200></canvas>
I would like to simply add a "sky" background in my scene so that it won't cover the objects but just being visible after them.
I've tried to, but I have no idea to do it without applying it over the other objects drawn in the scene.
Unfortunately I've to do all this without using any advanced library such as three.js and so on.
The background I'd like to use is in /Assets/sky.jpg
Here the js file:
var program0;
var program1;
var gl;
var shaderDir;
var baseDir;
var lastUpdateTime;
var boatModel;
var rockModel;
var rock2Model;
var oceanModel;
var object = [];
//attributes and uniforms
var positionAttributeLocation = Array();
var uvAttributeLocation = Array();
var matrixLocation = Array();
var textLocation = Array();
var normalAttributeLocation = Array();
var normalMatrixPositionHandle = Array();
var worldViewMatrixLocation = Array();
var worldViewMatrixLocation_transpose = Array();
var materialDiffColorHandle = Array();
var lightDirectionHandle = Array();
var lightColorHandle = Array();
var ambientLightcolorHandle = Array();
var specularColorHandle = Array();
var specShineHandle = Array();
var vaos = new Array();
var textures = new Array();
var modelStr = Array();
var modelTexture = Array();
//matrices
var viewMatrix;
var perspectiveMatrix;
//lights
//define directional light
var dirLightAlpha = -utils.degToRad(180);
var dirLightBeta = -utils.degToRad(100);
var directionalLight;
var directionalLightColor;
var ambientLight = [0.5, 0.5, 0.5];
var specularColor = [0.0, 0.0, 0.0];
var specShine = 0.0;
//camera
var cx = 0;
var cy = 0;
var cz = 1;
var camAngle = 0;
var camElev = 5;
//boat kinematics
var linearDir = 0;
var linearVel = 0;
var velX = 0;
var velZ = 0;
var maxLinearVel = 0.01;
var linearAcc = 0.0001;
var linearDrag = 0.005;
var turningDir = 0;
var angularVel = 0.0;
var maxAngularVel = 0.2;
var angularAcc = 0.01;
var angularDrag = 0.01;
modelStr[0] = 'Assets/Boat/Boat.obj';
modelStr[1] = 'Assets/Rocks/Rock1/rock1.obj';
modelStr[2] = 'Assets/Rocks/Rock2/Rock_1.obj';
modelStr[3] = 'Assets/ocean-obj/ocean.obj';
//modelStr[3] = 'Assets/ocean2/hdri-ca-sky.obj';
modelTexture[0] = 'Assets/Boat/textures/boat_diffuse.bmp';
modelTexture[1] = 'Assets/Rocks/Rock1/textures/rock_low_Base_Color.png';
modelTexture[2] = 'Assets/Rocks/Rock2/Rock_1_Tex/Rock_1_Base_Color.jpg';
modelTexture[3] = 'Assets/ocean-obj/woter.jpg';
//modelTexture[3] = 'Assets/ocean2/CA-Sky-2016-04-15-11-30-am.jpg';
modelTexture[4] = 'Assets/Sea/sea.jpg'
var nFrame = 0;
/***********************************************************************************************/
class Item {
x; y; z;
Rx; Ry; Rz;
S;
vertices;
normals;
indices;
texCoords;
materialColor;
constructor(x, y, z, Rx, Ry, Rz, S) {
this.x = x;
this.y = y;
this.z = z;
this.Rx = Rx;
this.Ry = Ry;
this.Rz = Rz;
this.S = S;
}
buildWorldMatrix() {
return utils.MakeWorld(this.x, this.y, this.z, this.Rx, this.Ry, this.Rz, this.S);
}
setAttr(objectVertices, objectNormals, objectIndices, objectTexCoords) {
this.vertices = objectVertices;
this.normals = objectNormals;
this.indices = objectIndices;
this.texCoords = objectTexCoords;
}
setMaterialColor(materialColorArray) {
this.materialColor = materialColorArray;
}
}
//objects
var rock = new Item(1.0, -0.5, -3.0, 0.0, 0.0, 0.0, 1.0 / 20.0);
var boat = new Item(0.0, -0.15, 0.0, 90.0, 0.0, 0.0, 1.0 / 1000.0);
var rock2 = new Item(-1.0, -0.4, -3, -30.0, 0.0, 0.0, 1.0 / 10.0);
var ocean = new Item(0.0, -0.02, 0.0, 90.0, 0.0, 0.0, 100.0);
function isPowerOf2(value) {
return (value & (value - 1)) == 0;
}
function main() {
utils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
setFloorCoord();
/* Load corresponding information from the models */
object[0] = boat;
object[1] = rock;
object[2] = rock2;
object[3] = ocean;
boat.setAttr(boatModel.vertices, boatModel.vertexNormals, boatModel.indices, boatModel.textures);
boat.setMaterialColor([1.0, 1.0, 1.0]); // set material color for boat
rock.setAttr(rockModel.vertices, rockModel.vertexNormals, rockModel.indices, rockModel.textures);
rock.setMaterialColor([1.0, 1.0, 1.0]); // set material color for rock
rock2.setAttr(rock2Model.vertices, rock2Model.vertexNormals, rock2Model.indices, rock2Model.textures);
rock2.setMaterialColor([1.0, 1.0, 1.0]);
ocean.setAttr(oceanModel.vertices, oceanModel.vertexNormals, oceanModel.indices, oceanModel.textures);
ocean.setMaterialColor([1.0, 1.0, 1.0]);
directionalLight = [Math.cos(dirLightAlpha) * Math.cos(dirLightBeta),
Math.sin(dirLightAlpha),
Math.cos(dirLightAlpha) * Math.sin(dirLightBeta)
];
directionalLightColor = [1.0, 1.0, 1.0];
/* Retrieve the position of the attributes and uniforms */
getShadersPos()
objectWorldMatrix = Array();
objectWorldMatrix[0] = boat.buildWorldMatrix(); //boat WorldMatrix
objectWorldMatrix[1] = rock.buildWorldMatrix(); //rock WorlMatrix
objectWorldMatrix[2] = rock2.buildWorldMatrix();
objectWorldMatrix[3] = ocean.buildWorldMatrix();
perspectiveMatrix = utils.MakePerspective(90, gl.canvas.width / gl.canvas.height, 0.1, 100.0);
viewMatrix = utils.MakeView(0.0, 1.0, 1.0, 15.0, 0.0);
setBuffers();
drawScene();
}
async function init() {
var path = window.location.pathname;
var page = path.split("/").pop();
baseDir = window.location.href.replace(page, '');
shaderDir = baseDir + "shaders/";
var canvas = document.getElementById("c");
lastUpdateTime = (new Date).getTime();
gl = canvas.getContext("webgl2");
if (!gl) {
document.write("GL context not opened");
return;
}
await utils.loadFiles([shaderDir + 'vs.glsl', shaderDir + 'fs.glsl'], function (shaderText) {
var vertexShader = utils.createShader(gl, gl.VERTEX_SHADER, shaderText[0]);
var fragmentShader = utils.createShader(gl, gl.FRAGMENT_SHADER, shaderText[1]);
program0 = utils.createProgram(gl, vertexShader, fragmentShader);
});
await utils.loadFiles([shaderDir + 'vs_unlit.glsl', shaderDir + 'fs_unlit.glsl'], function (shaderText) {
var vertexShader = utils.createShader(gl, gl.VERTEX_SHADER, shaderText[0]);
var fragmentShader = utils.createShader(gl, gl.FRAGMENT_SHADER, shaderText[1]);
program1 = utils.createProgram(gl, vertexShader, fragmentShader);
});
//###################################################################################
//This loads the obj model in the boatModel variable
var boatObjStr = await utils.get_objstr(baseDir + modelStr[0]);
boatModel = new OBJ.Mesh(boatObjStr);
//###################################################################################
//###################################################################################
//This loads the obj model in the rockModel variable
var rockObjStr = await utils.get_objstr(baseDir + modelStr[1]);
rockModel = new OBJ.Mesh(rockObjStr);
//###################################################################################
//###################################################################################
//This loads the obj model in the rockModel variable
var rock2ObjStr = await utils.get_objstr(baseDir + modelStr[2]);
rock2Model = new OBJ.Mesh(rock2ObjStr);
//###################################################################################
var oceanObjStr = await utils.get_objstr(baseDir + modelStr[3]);
oceanModel = new OBJ.Mesh(oceanObjStr);
initControls(canvas);
main();
}
function getShadersPos() {
positionAttributeLocation[0] = gl.getAttribLocation(program0, "a_position");
uvAttributeLocation[0] = gl.getAttribLocation(program0, "a_uv");
matrixLocation[0] = gl.getUniformLocation(program0, "matrix");
worldViewMatrixLocation[0] = gl.getUniformLocation(program0, "worldviewmatrix");
worldViewMatrixLocation_transpose[0] = gl.getUniformLocation(program0, "worldviewmatrix_t");
textLocation[0] = gl.getUniformLocation(program0, "u_texture");
normalAttributeLocation[0] = gl.getAttribLocation(program0, "inNormal");
normalMatrixPositionHandle[0] = gl.getUniformLocation(program0, 'nMatrix');
materialDiffColorHandle[0] = gl.getUniformLocation(program0, 'mDiffColor');
lightDirectionHandle[0] = gl.getUniformLocation(program0, 'lightDirection');
lightColorHandle[0] = gl.getUniformLocation(program0, 'lightColor');
ambientLightcolorHandle[0] = gl.getUniformLocation(program0, 'ambientLightcolor');
specularColorHandle[0] = gl.getUniformLocation(program0, 'specularColor');
specShineHandle[0] = gl.getUniformLocation(program0, 'SpecShine');
}
function setBuffers() {
for (let i = 0; i < object.length; i++) {
vaos[i] = gl.createVertexArray();
gl.bindVertexArray(vaos[i])
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(object[i].vertices), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionAttributeLocation[0]);
gl.vertexAttribPointer(positionAttributeLocation[0], 3, gl.FLOAT, false, 0, 0);
var uvBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(object[i].texCoords), gl.STATIC_DRAW);
gl.enableVertexAttribArray(uvAttributeLocation[0]);
gl.vertexAttribPointer(uvAttributeLocation[0], 2, gl.FLOAT, false, 0, 0);
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(object[i].indices), gl.STATIC_DRAW);
var normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(object[i].normals), gl.STATIC_DRAW);
gl.enableVertexAttribArray(normalAttributeLocation[0]);
gl.vertexAttribPointer(normalAttributeLocation[0], 3, gl.FLOAT, false, 0, 0);
textures[i] = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textures[i]);
image = new Image();
image.crossOrigin = "anonymous";
image.src = baseDir + modelTexture[i];
image.onload = function (texture, image) {
return function () {
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
// Check if the image is a power of 2 in both dimensions.
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
// Yes, it's a power of 2. Generate mips.
gl.generateMipmap(gl.TEXTURE_2D);
} else {
// No, it's not a power of 2. Turn off mips and set wrapping to clamp to edge
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
};
}(textures[i], image);
}
}
function drawObjects() {
for (let i = 0; i < object.length; ++i) {
gl.useProgram(program0);
var viewWorldMatrix = utils.multiplyMatrices(viewMatrix, objectWorldMatrix[i]);
var projectionMatrix = utils.multiplyMatrices(perspectiveMatrix, viewWorldMatrix);
gl.uniformMatrix4fv(matrixLocation[0], gl.FALSE, utils.transposeMatrix(projectionMatrix));
gl.uniformMatrix4fv(worldViewMatrixLocation_transpose[0], gl.FALSE, utils.transposeMatrix(utils.invertMatrix(utils.transposeMatrix(viewWorldMatrix))));
gl.uniformMatrix4fv(worldViewMatrixLocation[0], gl.FALSE, utils.transposeMatrix(viewWorldMatrix));
gl.uniformMatrix4fv(normalMatrixPositionHandle[0], gl.FALSE, utils.transposeMatrix(utils.invertMatrix(utils.transposeMatrix(objectWorldMatrix[i]))));
gl.uniform3fv(materialDiffColorHandle[0], object[i].materialColor);
gl.uniform3fv(lightColorHandle[0], directionalLightColor);
gl.uniform3fv(lightDirectionHandle[0], directionalLight);
gl.uniform3fv(ambientLightcolorHandle[0], ambientLight);
gl.uniform3fv(specularColorHandle[0], specularColor);
gl.uniform1f(specShineHandle[0], specShine);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textures[i]);
gl.uniform1i(textLocation[0], textures[i]);
gl.bindVertexArray(vaos[i]);
gl.drawElements(gl.TRIANGLES, object[i].indices.length, gl.UNSIGNED_SHORT, 0);
}
}
var counter = 0;
function animate(item) {
var currentTime = (new Date).getTime();
if (lastUpdateTime != null) {
boatDynamic(currentTime);
var deltaC = (30 * (currentTime - lastUpdateTime)) / 1000.0;
//item.z += deltaC/100;
//item.Rz += deltaC;
}
/* depending on which object we want to animate we change the worldmatrix of the object */
//objectWorldMatrix[0] = utils.MakeWorld(0.0, item.y, item.z, item.Rx, item.Ry, item.Rz, item.S);
counter += 0.005;
//item.z = counter % 2;
//item.y = counter;
//(0, -1, 2, 45, 0)
//item.z -= 0.002;
viewMatrix = utils.MakeView(cx + item.x, cy + 1, 2 + item.z, camElev, 0);
//<---- la barca si muove verso la z negativa
//item.y += 0.002;
objectWorldMatrix[0] = item.buildWorldMatrix();
//objectWorldMatrix[1] = rock.buildWorldMatrix();
//objectWorldMatrix[2] = rock2.buildWorldMatrix();
lastUpdateTime = currentTime;
}
function drawScene() {
animate(boat);
gl.clearColor(0.85, 0.85, 0.85, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
// DRAW THE OBJECTS IN THE SCENE
drawObjects();
window.requestAnimationFrame(drawScene);
}
//controls
var keys = [];
var vz = 0.0;
var rvy = 0.0;
var keyFunctionDown = function (e) {
if (!keys[e.keyCode]) {
keys[e.keyCode] = true;
switch (e.keyCode) {
case 37: //LEFT ARROW KEY DOWN
turningDir = - 1;
break;
case 39: //RIGHT ARROW KEY DOWN
turningDir = + 1;
break;
case 38: //UP ARROW KEY DOWN
linearDir = + 1;
break;
case 40: //DOWN ARROW KEY DOWN
linearDir = - 1;
break;
//camera controls
case 87:
camElev += 5;
console.log(camElev)
break;
case 83:
camElev -= 5;
console.log(camElev)
break;
}
}
}
var keyFunctionUp = function (e) {
if (keys[e.keyCode]) {
keys[e.keyCode] = false;
switch (e.keyCode) {
case 37: //LEFT ARROW KEY UP
turningDir = 0;
break;
case 39: //RIGHT ARROW KEY UP
turningDir = 0;
break;
case 38: //UP ARROW KEY UP
linearDir = 0;
break;
case 40: //DOWN ARROW KEY DOWN
linearDir = 0;
break;
}
}
}
function initControls(canvas) {
window.addEventListener("keyup", keyFunctionUp, false);
window.addEventListener("keydown", keyFunctionDown, false);
}
function boatDynamic(currentTime) {
//console.log(linearVel);
//boat turning
angularVel += turningDir * angularAcc;
if (Math.abs(angularVel) >= maxAngularVel)
angularVel = Math.sign(angularVel) * maxAngularVel;
//angular velocity degradation
angularVel = angularVel * (1 - angularDrag);
boat.Rx += angularVel;
//boat speed
linearVel += linearDir * linearAcc;
if (Math.abs(linearVel) >= maxLinearVel)
linearVel = Math.sign(linearVel) * maxLinearVel;
//linear vel degradation
linearVel = linearVel * (1 - linearDrag)
//linear velocity axis decomposition
velX = - linearVel * Math.cos(utils.degToRad(boat.Rx));
velZ = - linearVel * Math.sin(utils.degToRad(boat.Rx));
boat.x += velX;
boat.z += velZ;
//simple boat "wobbling" around its y axis, must be implemented better
if (Math.random() > 0.8) {
boat.Ry += Math.sin(utils.degToRad(currentTime)) / 8;
}
}
function dirLightChange(value, type) {
if (type == 'alpha')
dirLightAlpha = -utils.degToRad(value);
else
dirLightBeta = -utils.degToRad(value);
directionalLight = [Math.cos(dirLightAlpha) * Math.cos(dirLightBeta),
Math.sin(dirLightAlpha),
Math.cos(dirLightAlpha) * Math.sin(dirLightBeta)
];
drawObjects();
}
function onColorChange(value, type) {
let result = HEX2RGB(value);
var r = result[0] / 255.0;
var g = result[1] / 255.0;
var b = result[2] / 255.0;
if (type == 'ambient')
ambientLight = [r, g, b];
else if (type == 'directional')
directionalLightColor = [r, g, b];
else if (type == 'material')
boat.setMaterialColor([r, g, b]);
else
specularColor = [r, g, b];
drawObjects();
}
function onSpecShineChange(value) {
specShine = value;
drawObjects();
}
window.onload = init;
Here the repo with the entire project: repo
method 1 easiest
clear the depth buffer
turn off the depth test
draw a plane with your sky texture
turn on the depth test
draw your objects
method 2 (slightly more efficient)
clear the depth buffer
set depth func to LESS
turn on the depth test
draw your opaque objects
set depth func to LEQUAL
draw a plane with your sky texture at Z = 1
draw your transparent objects
Note: drawing a plane at Z = 1 is easiest with a custom shader that just does that.
Example: https://stackoverflow.com/a/52508687/128511
Many old outdated 3D engines try to use the system that draws everything else. In other words they only implement one thing, a loop that draws all objects with a single projection matrix and view matrix, so they have to compute a model matrix that positions the plane so that it just happens to show up at -Z in the current view and the current projection. That's silly IMO.
Another thing slightly less outdated 3D engines do is let each object the draw use a different projection and view matrix. In that case projections and view matrices for the planes can be set to something like (assuming the plane has vertices at Z = 0
view matrix = identity
projection matrix = [
1, 0, 0, 0
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 1, 1,
];
Will move Z to 1.
You then probably want to set the first 1 (width) and second 1 (height) as shown in this answer (same as above)
If it was me though I'd do what modern engines do and use a different shader like the one in the first link above just for drawing an image.
Note: you might also want to look into skyboxes
This is from a Khanacademy project - I was wondering why when I try to apply my repulsion force from my buglight to each of the flies why does it return __env__.flies[j].applyForce is not a function.
I had been messing around with this and can't get it to accept that force. Also when I try to make the glow of the buglight within that function change by adding an amount and then removing an amount it looks like it is happening too fast because it is only showing the maximum glow amount.
CODE:
// I created a light over where the mouse is and the flies (random start poisitions) are drawn toward the light, but I included some randomness to their movements to add some variation to their paths, the flies are repelled from the buglight using a repulsion force:
var Mover = function() {
this.position = new PVector (random(width),random(height));
this.velocity = new PVector (0,0);
this.acceleration = new PVector (0,0);
this.mass = 1;
};
Mover.prototype.update = function () {
var mouse = new PVector(mouseX,mouseY);
var dist = PVector.sub(mouse, this.position);
var randomPush = new PVector(random(-1,1),random(-1,1));
dist.normalize();
dist.mult(0.4);
this.acceleration = dist;
this.acceleration.add(randomPush);
this.velocity.add(this.acceleration);
this.velocity.limit(9);
this.position.add(this.velocity);
};
Mover.prototype.display = function () {
strokeWeight(7);
stroke(0, 0, 0);
point(this.position.x,this.position.y);
strokeWeight(5);
point(this.position.x - 3,this.position.y -3);
point(this.position.x + 3,this.position.y -3);
};
var Buglight = function (){
this.position = new PVector (random(width-50),random(height-80));
this.velocity = new PVector (0,0);
this.acceleration = new PVector (0,0);
this.glow = 70;
this.mass = 20;
this.G = 1;
};
Buglight.prototype.update = function() {
noStroke();
fill(39, 131, 207,90);
ellipse(this.position.x+20,this.position.y+35,this.glow,this.glow);
};
Buglight.prototype.repulsion = function(fly) {
var force = PVector.sub(this.position,fly.position);
var distance = force.mag();
distance = constrain(distance, 4.0, 20.0);
force.normalize();
var strength = -1 *(this.G * this.mass * fly.mass) / (distance * distance);
force.mult(strength);
return force;
};
Buglight.prototype.display = function() {
noStroke();
fill(5, 170, 252);
rect(this.position.x+15,this.position.y,10,60);
fill(0, 0, 0);
noStroke();
rect(this.position.x-10,this.position.y+60,60,10,10);
rect(this.position.x,this.position.y,10,60);
rect(this.position.x+30,this.position.y,10,60 );
quad(this.position.x,this.position.y,
this.position.x-10,this.position.y+20,
this.position.x+50,this.position.y+20,
this.position.x+40,this.position.y);
};
Buglight.prototype.checkEdges = function () {
};
var flies = [];
for (var i = 0; i < 4; i++){
flies[i] = new Mover();
}
var buglight = new Buglight();
var fly = new Mover();
draw = function() {
background(71, 71, 71);
strokeWeight(56);
stroke(219, 247, 8,100);
fill(255, 238, 0);
ellipse(mouseX,mouseY,20,20);
buglight.update();
buglight.display();
for (var j = 0; j < flies.length; j++) {
var blueForce = buglight.repulsion(flies[j]);
flies[j].applyForce(blueForce);
flies[j].update();
flies[j].display();
}
};
There is no applyForce function on your Mover class. Something is missing I feel.
I'm trying to create a game in canvas with javascript where you control a spaceship and have it so that the canvas will translate and rotate to make it appear like the spaceship is staying stationary and not rotating.
Any help would be greatly appreciated.
window.addEventListener("load",eventWindowLoaded, false);
function eventWindowLoaded() {
canvasApp();
}
function canvasSupport() {
return Modernizr.canvas;
}
function canvasApp() {
if (!canvasSupport()) {
return;
}
var theCanvas = document.getElementById("myCanvas");
var height = theCanvas.height; //get the heigth of the canvas
var width = theCanvas.width; //get the width of the canvas
var context = theCanvas.getContext("2d"); //get the context
var then = Date.now();
var bgImage = new Image();
var stars = new Array;
bgImage.onload = function() {
context.translate(width/2,height/2);
main();
}
var rocket = {
xLoc: 0,
yLoc: 0,
score : 0,
damage : 0,
speed : 20,
angle : 0,
rotSpeed : 1,
rotChange: 0,
pointX: 0,
pointY: 0,
setScore : function(newScore){
this.score = newScore;
}
}
function Star(){
var dLoc = 100;
this.xLoc = rocket.pointX+ dLoc - Math.random()*2*dLoc;
this.yLoc = rocket.pointY + dLoc - Math.random()*2*dLoc;
//console.log(rocket.xLoc+" "+rocket.yLoc);
this.draw = function(){
drawStar(this.xLoc,this.yLoc,20,5,.5);
}
}
//var stars = new Array;
var drawStars = function(){
context.fillStyle = "yellow";
if (typeof stars !== 'undefined'){
//console.log("working");
for(var i=0;i< stars.length ;i++){
stars[i].draw();
}
}
}
var getDistance = function(x1,y1,x2,y2){
var distance = Math.sqrt(Math.pow((x2-x1),2)+Math.pow((y2-y1),2));
return distance;
}
var updateStars = function(){
var numStars = 10;
while(stars.length<numStars){
stars[stars.length] = new Star();
}
for(var i=0; i<stars.length; i++){
var tempDist = getDistance(rocket.pointX,rocket.pointY,stars[i].xLoc,stars[i].yLoc);
if(i == 0){
//console.log(tempDist);
}
if(tempDist > 100){
stars[i] = new Star();
}
}
}
function drawRocket(xLoc,yLoc, rWidth, rHeight){
var angle = rocket.angle;
var xVals = [xLoc,xLoc+(rWidth/2),xLoc+(rWidth/2),xLoc-(rWidth/2),xLoc-(rWidth/2),xLoc];
var yVals = [yLoc,yLoc+(rHeight/3),yLoc+rHeight,yLoc+rHeight,yLoc+(rHeight/3),yLoc];
for(var i = 0; i < xVals.length; i++){
xVals[i] -= xLoc;
yVals[i] -= yLoc+rHeight;
if(i == 0){
console.log(yVals[i]);
}
var tempXVal = xVals[i]*Math.cos(angle) - yVals[i]*Math.sin(angle);
var tempYVal = xVals[i]*Math.sin(angle) + yVals[i]*Math.cos(angle);
xVals[i] = tempXVal + xLoc;
yVals[i] = tempYVal+(yLoc+rHeight);
}
rocket.pointX = xVals[0];
rocket.pointY = yVals[0];
//rocket.yLoc = yVals[0];
//next rotate
context.beginPath();
context.moveTo(xVals[0],yVals[0])
for(var i = 1; i < xVals.length; i++){
context.lineTo(xVals[i],yVals[i]);
}
context.closePath();
context.lineWidth = 5;
context.strokeStyle = 'blue';
context.stroke();
}
var world = {
//pixels per second
startTime: Date.now(),
speed: 50,
startX:width/2,
startY:height/2,
originX: 0,
originY: 0,
xDist: 0,
yDist: 0,
rotationSpeed: 20,
angle: 0,
distance: 0,
calcOrigins : function(){
world.originX = -world.distance*Math.sin(world.angle*Math.PI/180);
world.originY = -world.distance*Math.cos(world.angle*Math.PI/180);
}
};
var keysDown = {};
addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false);
addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);
var update = function(modifier) {
if (37 in keysDown) { // Player holding left
rocket.angle -= rocket.rotSpeed* modifier;
rocket.rotChange = - rocket.rotSpeed* modifier;
//console.log("left");
}
if (39 in keysDown) { // Player holding right
rocket.angle += rocket.rotSpeed* modifier;
rocket.rotChange = rocket.rotSpeed* modifier;
//console.log("right");
}
};
var render = function (modifier) {
context.clearRect(-width*10,-height*10,width*20,height*20);
var dX = (rocket.speed*modifier)*Math.sin(rocket.angle);
var dY = (rocket.speed*modifier)*Math.cos(rocket.angle);
rocket.xLoc += dX;
rocket.yLoc -= dY;
updateStars();
drawStars();
context.translate(-dX,dY);
context.save();
context.translate(-rocket.pointX,-rocket.pointY);
context.translate(rocket.pointX,rocket.pointY);
drawRocket(rocket.xLoc,rocket.yLoc,50,200);
context.fillStyle = "red";
context.fillRect(rocket.pointX,rocket.pointY,15,5);
//context.restore(); // restores the coordinate system back to (0,0)
context.fillStyle = "green";
context.fillRect(0,0,10,10);
context.rotate(rocket.angle);
context.restore();
};
function drawStar(x, y, r, p, m)
{
context.save();
context.beginPath();
context.translate(x, y);
context.moveTo(0,0-r);
for (var i = 0; i < p; i++)
{
context.rotate(Math.PI / p);
context.lineTo(0, 0 - (r*m));
context.rotate(Math.PI / p);
context.lineTo(0, 0 - r);
}
context.fill();
context.restore();
}
// the game loop
function main(){
requestAnimationFrame(main);
var now = Date.now();
var delta = now - then;
update(delta / 1000);
//now = Date.now();
//delta = now - then;
render(delta / 1000);
then = now;
// Request to do this again ASAP
}
var w = window;
var requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame;
//start the game loop
//gameLoop();
//event listenters
bgImage.src = "images/background.jpg";
} //canvasApp()
Origin
When you need to rotate something in canvas it will always rotate around origin, or center for the grid if you like where the x and y axis crosses.
You may find my answer here useful as well
By default the origin is in the top left corner at (0, 0) in the bitmap.
So in order to rotate content around a (x,y) point the origin must first be translated to that point, then rotated and finally (and usually) translated back. Now things can be drawn in the normal order and they will all be drawn rotated relative to that rotation point:
ctx.translate(rotateCenterX, rotateCenterY);
ctx.rotate(angleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
Absolute angles and positions
Sometimes it's easier to keep track if an absolute angle is used rather than using an angle that you accumulate over time.
translate(), transform(), rotate() etc. are accumulative methods; they add to the previous transform. We can set absolute transforms using setTransform() (the last two arguments are for translation):
ctx.setTransform(1, 0, 0, 1, rotateCenterX, rotateCenterY); // absolute
ctx.rotate(absoluteAngleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
The rotateCenterX/Y will represent the position of the ship which is drawn untransformed. Also here absolute transforms can be a better choice as you can do the rotation using absolute angles, draw background, reset transformations and then draw in the ship at rotateCenterX/Y:
ctx.setTransform(1, 0, 0, 1, rotateCenterX, rotateCenterY);
ctx.rotate(absoluteAngleInRadians);
ctx.translate(-rotateCenterX, -rotateCenterY);
// update scene/background etc.
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transforms
ctx.drawImage(ship, rotateCenterX, rotateCenterY);
(Depending on orders of things you could replace the first line here with just translate() as the transforms are reset later, see demo for example).
This allows you to move the ship around without worrying about current transforms, when a rotation is needed use the ship's current position as center for translation and rotation.
And a final note: the angle you would use for rotation would of course be the counter-angle that should be represented (ie. ctx.rotate(-angle);).
Space demo ("random" movements and rotations)
The red "meteors" are dropping in one direction (from top), but as the ship "navigates" around they will change direction relative to our top view angle. Camera will be fixed on the ship's position.
(ignore the messy part - it's just for the demo setup, and I hate scrollbars... focus on the center part :) )
var img = new Image();
img.onload = function() {
var ctx = document.querySelector("canvas").getContext("2d"),
w = 600, h = 400, meteors = [], count = 35, i = 0, x = w * 0.5, y, a = 0, a2 = 0;
ctx.canvas.width = w; ctx.canvas.height = h; ctx.fillStyle = "#555";
while(i++ < count) meteors.push(new Meteor());
(function loop() {
ctx.clearRect(0, 0, w, h);
y = h * 0.5 + 30 + Math.sin((a+=0.01) % Math.PI*2) * 60; // ship's y and origin's y
// translate to center of ship, rotate, translate back, render bg, reset, draw ship
ctx.translate(x, y); // translate to origin
ctx.rotate(Math.sin((a2+=0.005) % Math.PI) - Math.PI*0.25); // rotate some angle
ctx.translate(-x, -y); // translate back
ctx.beginPath(); // render some moving meteors for the demo
for(var i = 0; i < count; i++) meteors[i].update(ctx); ctx.fill();
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transforms
ctx.drawImage(img, x - 32, y); // draw ship as normal
requestAnimationFrame(loop); // loop animation
})();
};
function Meteor() { // just some moving object..
var size = 5 + 35 * Math.random(), x = Math.random() * 600, y = -200;
this.update = function(ctx) {
ctx.moveTo(x + size, y); ctx.arc(x, y, size, 0, 6.28);
y += size * 0.5; if (y > 600) y = -200;
};
}
img.src = "http://i.imgur.com/67KQykW.png?1";
body {background:#333} canvas {background:#000}
<canvas></canvas>