Related
I am creating a simple WebGL program that creates a cube rotating around the Z axis. The problem is that when its rotating some sides of it override the others and you don't see each individual side of the cube. I looked for a solution online but it makes it worse. They enable the face culling and for them, it seems to work but for me, unfortunately, it doesn't. Can anyone tell me what do I need to add into my code in order to fix the face overriding?
Full Source Code:
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
var a = 24;
var positions = [
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0.5, 0.5, 0.0,
-0.5, 0.5, 0.0,
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5,
0.5, 0.5, 0.5,
-0.5, 0.5, 0.5,
//0.5, 0.5, 1.0,
];
var indices = [
0, 1, 2, 0, 2, 3, // Front
0, 4, 7, 0, 3, 7, // Side 1
1, 5, 6, 1, 2, 6, // Side 2
4, 5, 6, 6, 7, 4 // Back
];
var rotation_angle = 1;
var radians = (rotation_angle * Math.PI)/180;
var rotation = [Math.sin(radians), 0.0, Math.cos(radians)];
var _buffer = gl.createBuffer();
var i_buffer = gl.createBuffer();
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
precision mediump float;
attribute vec3 position;
uniform vec3 rotation;
varying vec3 f_col;
void main(){
vec2 newPos = vec2(
position.x * rotation.x - position.z * rotation.z,
position.x * rotation.z + position.z * rotation.x
);
gl_Position = vec4(newPos.x, position.y, newPos.y, 1.0);
f_col = position;
}
`);
gl.compileShader(vertexShader);
// Check if it compiled
var success2 = gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS);
if (!success2) {
// Something went wrong during compilation; get the error
throw "could not compile shader:" + gl.getShaderInfoLog(vertexShader);
}
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, `
precision mediump float;
varying vec3 f_col;
void main() {
gl_FragColor = vec4(f_col.x, 0.2, f_col.z, 1.0);
}
`);
gl.compileShader(fragmentShader);
var success1 = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS);
if (!success1) {
// Something went wrong during compilation; get the error
throw "could not compile shader:" + gl.getShaderInfoLog(fragmentShader);
}
var program = gl.createProgram();
// Attach pre-existing shaders
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var attributeLoc = gl.getAttribLocation(program, "position");
gl.bindBuffer(gl.ARRAY_BUFFER, _buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions),
gl.STATIC_DRAW);
gl.vertexAttribPointer(attributeLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(attributeLoc);
gl.useProgram(program);
var rotation_location = gl.getUniformLocation(program, "rotation");
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, i_buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices),
gl.STATIC_DRAW);
function loop() {
gl.clearColor(0.75, 0.85, 0.8, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//gl.drawArrays(gl.TRIANGLE_FAN, 0, 8);
gl.drawElements(gl.TRIANGLES, a, gl.UNSIGNED_SHORT, 0);
//gl.drawArrays(gl.TRIANGLE_STRIP, 1, 3);
rotation_angle++;
var radians = (rotation_angle * Math.PI)/180;
var rotation = [Math.sin(radians), 0.0, Math.cos(radians)];
gl.uniform3fv(rotation_location, rotation);
requestAnimationFrame(loop);
}
loop();
<canvas id="canvas" width="500" height="500"></canvas>
I'm trying to change my triangle color but it doesn't happen.When i press 'k' is has to be random color.Is there any way to do this task basically or different ways.Here is my code;
<!DOCTYPE html>
<html>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
void main() {
gl_Position = vPosition;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
</script>
<script type="text/javascript" src="webgl-utils.js"></script>
<script type="text/javascript" src="initShaders.js"></script>
<script type="text/javascript" src="MV.js"></script>
<script type="text/javascript" src="b.js"></script>
<body>
<canvas id="gl-canvas" width="640" height="360" style="border: solid;"></canvas>
</body>
And my script code;
var gl;
var color = [0.0, 0.0, 1.0, 1];
window.onload = function init() {
var triangle = new Float32Array(
[ 0.2 , -0.4,
0.8, -0.4,
0.5, 0.5 ]);
var canvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
if (!gl) { alert("WebGL isn’t available"); }
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
program = initShaders(gl, "vertex-shader", "fragment-shader");
gl.useProgram(program);
var colorLocation = gl.getUniformLocation(program, "u_color");
gl.uniform4fv(colorLocation, color);
var vbuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
gl.bufferData( gl.ARRAY_BUFFER, triangle, gl.STATIC_DRAW );
var vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
window.addEventListener("keydown", checkKeyPressed); }
function checkKeyPressed(e) {
if (e.keyCode == "84") {
color = [Math.random(), Math.random(), Math.random(), 1];
alert("t pressed");
}
}
When i press t it shows me alert but doesn't change the triangle's color.
Your triangle is rendered only once. You have to redraw the triangle when the key was pressed.
Create a function which sets the value of the color uniform variable and renders the triangle:
function render(time_ms) {
gl.clear(gl.COLOR_BUFFER_BIT);
var colorLocation = gl.getUniformLocation(program, "u_color");
gl.uniform4fv(colorLocation, color);
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
Use requestAnimationFrame to trigger the render function. See also WebGLFundamentals - Animation:
function checkKeyPressed(e) {
if (e.keyCode == "84") {
color = [Math.random(), Math.random(), Math.random(), 1];
alert("t pressed");
requestAnimationFrame( render );
}
}
Note, alternatively you can continuously redraw the triangle, too:
function render(time_ms) {
gl.clear(gl.COLOR_BUFFER_BIT);
var colorLocation = gl.getUniformLocation(program, "u_color");
gl.uniform4fv(colorLocation, color);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame( render );
}
I applied the changes to your original code, see the example:
var gl, program;
var color = [0.0, 0.0, 1.0, 1];
window.onload = function init() {
var triangle = new Float32Array(
[ 0.2 , -0.4,
0.8, -0.4,
0.5, 0.5 ]);
var canvas = document.getElementById("gl-canvas");
gl = canvas.getContext( "experimental-webgl" );
if (!gl) { alert("WebGL isn’t available"); }
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
program = initShaders(gl, "vertex-shader", "fragment-shader");
gl.useProgram(program);
var colorLocation = gl.getUniformLocation(program, "u_color");
gl.uniform4fv(colorLocation, color);
var vbuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
gl.bufferData( gl.ARRAY_BUFFER, triangle, gl.STATIC_DRAW );
var vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
window.addEventListener("keydown", checkKeyPressed);
requestAnimationFrame( render );
}
function render(time_ms) {
gl.clear(gl.COLOR_BUFFER_BIT);
var colorLocation = gl.getUniformLocation(program, "u_color");
gl.uniform4fv(colorLocation, color);
gl.drawArrays(gl.TRIANGLES, 0, 3);
requestAnimationFrame( render );
}
function checkKeyPressed(e) {
if (e.keyCode == "84") {
color = [Math.random(), Math.random(), Math.random(), 1];
}
}
function initShaders(gl, vertexShaderId, fragmentShaderId) {
var vertShdr;
var fragShdr;
var vertElem = document.getElementById(vertexShaderId);
if (!vertElem) {
alert("Unable to load vertex shader " + vertexShaderId);
return -1;
} else {
vertShdr = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShdr, vertElem.text);
gl.compileShader(vertShdr);
if (!gl.getShaderParameter(vertShdr, gl.COMPILE_STATUS)) {
var msg = "Vertex shader failed to compile. The error log is:" + "<pre>" + gl.getShaderInfoLog(vertShdr) + "</pre>";
alert(msg);
console.log(msg);
return -1;
}
}
var fragElem = document.getElementById(fragmentShaderId);
if (!fragElem) {
alert("Unable to load vertex shader " + fragmentShaderId);
return -1;
} else {
fragShdr = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShdr, fragElem.text);
gl.compileShader(fragShdr);
if (!gl.getShaderParameter(fragShdr, gl.COMPILE_STATUS)) {
var msg = "Fragment shader failed to compile. The error log is:" + "<pre>" + gl.getShaderInfoLog(fragShdr) + "</pre>";
alert(msg);
console.log(msg);
return -1;
}
}
var program = gl.createProgram();
gl.attachShader(program, vertShdr);
gl.attachShader(program, fragShdr);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
var msg = "Shader program failed to link. The error log is:" + "<pre>" + gl.getProgramInfoLog(program) + "</pre>";
alert(msg);
console.log(msg);
return -1;
}
return program;
}
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
void main() {
gl_Position = vPosition;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
</script>
<canvas id="gl-canvas" width="640" height="360" style="border: solid;"></canvas>
I'm using WebGL2 and trying to call gl.drawArraysInstanced for drawing multiple shapes. The first call of gl.drawArraysInstanced can draw 2 or more elements.
gl.drawArraysInstanced(gl.TRIANGLES, 0, numVertices, 3);
I then call the same function 3 sec later but it only draws one triangle. (see example below)
var canvas = document.getElementById('canvas');
gl = canvas.getContext('webgl2');
var cube = {};
var vertices = [
-0.2, -0.2, 0,
0.2, -0.2, 0,
0.2, 0.2, 0,
];
var numVertices = vertices.length / 3;
var vertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
var vertexShader = getAndCompileShader('vertexShader', 'vertex');
var fragmentShader = getAndCompileShader('fragmentShader', 'fragment');
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
var vao = gl.createVertexArray();
gl.bindVertexArray(vao);
var positionAttributeLocation = gl.getAttribLocation(shaderProgram, 'position');
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
function getAndCompileShader(id, shaderType) {
var shader;
var shaderElement = document.getElementById(id);
var shaderText = shaderElement.text.trim();
shader = gl.createShader(
shaderType==='vertex' ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER
);
gl.shaderSource(shader, shaderText);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(shader));
return true;
}
console.log('Shader ' + id + ' compiled.')
return shader;
}
function start() {
gl.clearColor(.5,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindVertexArray(vao);
gl.drawArraysInstanced(gl.TRIANGLES, 0, numVertices, 2);
console.log('Drawing 2 triangles');
setTimeout(function() {
gl.clearColor(.5,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArraysInstanced(gl.TRIANGLES, 0, numVertices, 2);
console.log('Drawing 2 triangles again');
}, 3000);
}
start();
<script id="vertexShader" type="x-shader/x-vertex">
#version 300 es
in vec3 position;
void main() {
if(gl_InstanceID==0) {
gl_Position = vec4(position.x - 0.2, position.yz, 1);
} else {
gl_Position = vec4(position.x + 0.2, position.yz, 1);
}
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
#version 300 es
precision mediump float;
out vec4 finalColor;
void main() {
finalColor = vec4(0.5,0.5,0.5,1.0);;
}
</script>
<body>
<canvas id="canvas" style="width: 300px; height: 200px" width="600" height="400"></canvas>
</body>
Also, is calling gl.drawArraysInstanced supposed to be more efficient than gl.drawArrays?
Thanks
Once the drawing is complete, the gl.useProgram(null) needs to be called.
gl.enable(gl.DEPTH_TEST);
gl.clearColor(.5, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindVertexArray(vao);
gl.drawArraysInstanced(gl.TRIANGLES, 0, numVertices, 2);
gl.useProgram(null);
console.log('Drawing 2 triangles');
setInterval(function () {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(shaderProgram);
gl.drawArraysInstanced(gl.TRIANGLES, 0, numVertices, 2);
gl.useProgram(null);
console.log('Drawing 2 triangles again');
}, 3000);
I'm trying to use textures for the first time in WebGL and I'm having a problem getting it to work. I'm trying to apply a stone-like texture to cubes that are moving around in a 3D space, as you can see in the rocks.js below. I always get this error which I don't know what to make of (I'm using Chrome btw):
RENDER WARNING: there is no texture bound to the unit 0
Here is my index.html file:
<html>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec4 vColor;
attribute vec2 vTexCoord;
varying vec2 fTexCoord;
varying vec4 fColor;
uniform mat4 projection;
uniform mat4 modelview;
void main()
{
fTexCoord = vTexCoord;
fColor = vColor;
gl_Position = projection * modelview * vPosition;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fColor;
varying vec2 fTexCoord;
uniform sampler2D texture;
void
main()
{
gl_FragColor = texture2D( texture, fTexCoord );
//gl_FragColor = fColor;
}
</script>
<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="main.js"></script>
<script type="text/javascript" src="grid.js"></script>
<script type="text/javascript" src="spaceship.js"></script>
<script type="text/javascript" src="rocks.js"></script>
<body>
<canvas id="gl-canvas" width="1100" height="1200" style="float:left;">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</div>
</body>
</html>
Here is my rocks.js where the object which I'm attempting to apply texture to is defined:
// global webgl variables
var gl;
var vBuffer;
var cBuffer;
var iBuffer;
var tBuffer;
var mvLoc;
var rocks = (function() {
var direction = vec3();
var lastDirection = vec3();
var location = vec3();
var lastLocation = vec3();
var rock = {
NumVertices : 36,
indices :[
1, 0, 3,
3, 2, 1,
2, 3, 7,
7, 6, 2,
3, 0, 4,
4, 7, 3,
6, 5, 1,
1, 2, 6,
4, 5, 6,
6, 7, 4,
5, 4, 0,
0, 1, 1
],
vertices : [
vec4( -0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, 0.5, 0.5, 1.0 ),
vec4( 0.5, -0.5, 0.5, 1.0 ),
vec4( -0.5, -0.5, -0.5, 1.0 ),
vec4( -0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, 0.5, -0.5, 1.0 ),
vec4( 0.5, -0.5, -0.5, 1.0 )
],
texVertices : [
vec2(-0.5, -0.5),
vec2(-0.5, 0.5 ),
vec2( 0.5, 0.5 ),
vec2( 0.5, -0.5 ),
vec2( -0.5, -0.5 ),
vec2( -0.5, 0.5 ),
vec2( 0.5, 0.5 ),
vec2( 0.5, -0.5 )
],
render : function(mv) {
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, flatten(this.vertices));
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuffer);
gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, new Uint8Array(this.indices))
gl.bindBuffer( gl.ARRAY_BUFFER, tBuffer);
gl.bufferSubData( gl.ARRAY_BUFFER, 0, flatten(this.texVertices) );
mv = mult( mv, scalem(0.2, 0.2, 0.2) );
mv = mult( mv, translate(0.0, 0.0, 0.0) );
gl.uniformMatrix4fv(mvLoc, false, flatten(mv));
gl.drawElements( gl.TRIANGLES, this.NumVertices, gl.UNSIGNED_BYTE, 0 );
}
}
var init = function(glIn, vBufferIn, iBufferIn, tBufferIn, mvLocIn) {
// set global webgl variagles
gl = gl;
vBuffer = vBufferIn;
iBuffer = iBufferIn;
mvLoc = mvLocIn;
tBuffer = tBufferIn;
direction = [(Math.random()*0.06-0.03)/2, (Math.random()*0.06-0.03)/2, (Math.random()*0.06-0.03)/2];
location = [Math.random()*6-3, Math.random()*6-3, Math.random()*6-3];
lastDirection = direction;
lastLocation = location;
}
var render = function(spinX, spinY, mv) {
mv = mult( mv, rotateX(spinX) );
mv = mult( mv, rotateY(spinY) );
mv = mult( mv, translate(location) );
rock.render(mv);
}
var updateRock = function() {
// direction = updatedDir;
location = add( location, direction );
if(location[0] > 3 || location[0] < -3) location[0] = -location[0];
if(location[1] > 3 || location[1] < -3) location[1] = -location[1];
if(location[2] > 3 || location[2] < -3) location[2] = -location[2];
lastDirection = direction;
lastLocation = location;
}
return {
init : init,
render : render,
update : updateRock
};
});
And finally, my main.js for running everyting:
// webgl global variables
var gl;
var canvas;
var texture;
var texCoords = [];
var movement = false;
var spinX = 0;
var spinY = 0;
var origX;
var origY;
var xRot = 0;
var yRot = 0;
var grid;
var spaceship;
var rocksArray = [];
function configureTexture( image ) {
texture = gl.createTexture();
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 );
gl.generateMipmap( gl.TEXTURE_2D );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR );
}
window.onload = function init()
{
canvas = document.getElementById( "gl-canvas" );
gl = WebGLUtils.setupWebGL( canvas );
if ( !gl ) { alert( "WebGL isn't available" ); }
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 0.0, 0.0, 0.0, 0.0 );
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
var program = initShaders( gl, "vertex-shader", "fragment-shader" );
gl.useProgram( program );
var iBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 36, gl.DYNAMIC_DRAW);
var cBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, cBuffer );
gl.bufferData( gl.ARRAY_BUFFER, 128, gl.DYNAMIC_DRAW );
var vColor = gl.getAttribLocation( program, "vColor" );
gl.vertexAttribPointer( vColor, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vColor );
var vBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, vBuffer );
gl.bufferData( gl.ARRAY_BUFFER, 1024, gl.DYNAMIC_DRAW );
var vPosition = gl.getAttribLocation( program, "vPosition" );
gl.vertexAttribPointer( vPosition, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vPosition );
var tBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, tBuffer);
gl.bufferData( gl.ARRAY_BUFFER, 128, gl.STATIC_DRAW );
var vTexCoord = gl.getAttribLocation( program, "vTexCoord" );
gl.vertexAttribPointer( vTexCoord, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vTexCoord );
var image = new Image();
image.src = "rock.jpg"
image.onload = function() {
configureTexture( image );
}
var proLoc = gl.getUniformLocation( program, "projection" );
mvLoc = gl.getUniformLocation( program, "modelview" );
gl.uniform1i(gl.getUniformLocation(program, "texture"), 0);
var pers = perspective( 100.0, 1.0, 0.2, 100.0 );
gl.uniformMatrix4fv(proLoc, false, flatten(pers));
grid = new grid();
grid.init(gl, vBuffer, cBuffer, iBuffer, mvLoc);
spaceship = new spaceship();
spaceship.init(gl, vBuffer, cBuffer, iBuffer, mvLoc);
for (var i = 0; i < 100; i++) {
rocksArray[i] = new rocks();
rocksArray[i].init(gl, vBuffer, iBuffer, tBuffer, mvLoc);
}
attachEventHandlers();
render();
}
function reloadPage() {
location.reload();
}
function attachEventHandlers() {
canvas.addEventListener("mousedown", function(e){
movement = true;
origX = e.offsetX;
origY = e.offsetY;
// Disable drag and drop
e.preventDefault();
});
canvas.addEventListener("mouseup", function(e){
movement = false;
});
canvas.addEventListener("mousemove", function(e){
if(movement) {
spinY += (e.offsetX - origX) % 360;
spinX += (e.offsetY - origY) % 360;
origX = e.offsetX;
origY = e.offsetY;
}
});
window.addEventListener("keydown", function(e) {
spaceship.keyArray[e.keyCode] = true;
});
window.addEventListener("keyup", function(e) {
spaceship.keyArray[e.keyCode] = false;
});
}
function render() {
spaceship.moveSpaceship();
rocksArray.forEach(function(rock) {
rock.update();
});
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mv = lookAt( vec3(0.0, 0.0, 1.0), vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0) );
mv = mult(mv, rotateX(90));
// spaceship.render(spinX, spinY, mv);
mv = mult( mv, translate(-xVal, -yVal, -zVal));
mv = mult(mv, rotateZ(-zSpin));
mv = mult(mv, rotateX(-xSpin));
grid.render(spinX, spinY, mv);
rocksArray.forEach(function(rock) {
rock.render(spinX, spinY, mv);
});
requestAnimFrame( render );
}
The issue is most likely you start rendering immediately but your texture doesn't get created until the image has downloaded.
My suggestion is to create a 1x1 pixel texture to start, then replace the contents of that texture with the image once it has downloaded.
function configureTexture( image ) {
// texture = gl.createTexture(); -- delete this line
...
}
And change your initialization to something like
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// put a 1x1 red pixel in the texture so it's renderable immediately
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA,
gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));
// now load the image, the image will replace the contents of the
// texture once it has finished downloading
var image = new Image();
image.src = "rock.jpg"
image.onload = function() {
configureTexture( image );
}
I'd suggest taking a look at these tutorials
For some reason why i map the texture to the boxes i have drawn on the 3d canvas it is not showing correctly, all i get is a blue box and not the full texture.
<script id="shader-fs" type="x-shader/x-fragment" type="text/javascript">
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main(void) {
gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
}
</script>
<script id="shader-vs" type="x-shader/x-vertex" type="text/javascript">
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying vec2 vTextureCoord;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
}
</script>
<script type="text/javascript">
var gl;
var neheTexture;
function initGL(canvas) {
try {
gl = canvas.getContext("experimental-webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
} catch (e) {
}
if (!gl) {
alert("Could not initialise WebGL, sorry :-(");
}
}
function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
var str = "";
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3) {
str += k.textContent;
}
k = k.nextSibling;
}
var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}
gl.shaderSource(shader, str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
var shaderProgram;
function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
}
var mvMatrix = mat4.create();
var pMatrix = mat4.create();
function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}
var squareVertexPositionBuffer;
function initBuffers() {
squareVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
vertices = [
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
squareVertexPositionBuffer.itemSize = 3;
squareVertexPositionBuffer.numItems = 4;
}
var z = -50.0;
var eye = vec3.create([0, 0, z]); // negation of actual eye position
var pvMatrix = mat4.create();
var pvMatrixInverse = mat4.create();
function drawScene() {
var canvas = document.getElementById("MyCanvas");
var widthamount = Math.round(canvas.width/20);
var heightamount = Math.round(canvas.height / 20);
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(90, gl.viewportWidth / gl.viewportHeight, 0.1, 3000.0, pMatrix);
mat4.identity(mvMatrix);
// calculate the view transform mvMatrix, and the projection-view matrix pvMatrix
mat4.translate(mvMatrix, eye);
mat4.multiply(pMatrix, mvMatrix, pvMatrix);
var startHeight = -((heightamount * 2.1) / 2);
var startWidth = -((widthamount * 2.1) / 2);
mat4.translate(mvMatrix, [startWidth, startHeight, 0.0]);
for (i = 0; i < heightamount; ++i) {
for (q = 0; q < widthamount; ++q) {
mat4.translate(mvMatrix, [2.1, 0.0, 0.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, neheTexture);
gl.uniform1i(shaderProgram.samplerUniform, 0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
}
mat4.translate(mvMatrix, [-(widthamount*2.1), 2.1, 0.0]);
}
}
function webGLStart() {
resizeCanvas();
var canvas = document.getElementById("MyCanvas");
initGL(canvas);
initShaders();
initBuffers();
initTexture();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
window.addEventListener('resize', resizeCanvas, false);
tick();
}
function resizeCanvas() {
var canvas = document.getElementById("MyCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
//drawScene();
}
function handleLoadedTexture(texture) {
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, texture.image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);
}
function initTexture() {
neheTexture = gl.createTexture();
neheTexture.image = new Image();
neheTexture.image.onload = function () {
handleLoadedTexture(neheTexture)
}
neheTexture.image.src = "nehe.gif";
}
</script>
Image for texture looks like a full texture https://github.com/gpjt/webgl-lessons/blob/master/lesson05/nehe.gif
However boxes turn out to show like a blue box, i need 10 rep to put images :(((
You have no texture coordinates.
You need to set up a buffer with texture coordinates
squareTextureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareTextureCoordBuffer);
texcoords = [
1.0, 1.0,
0.0, 1.0,
1.0, 0.0,
0.0, 0.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW);
Then you're going to need to look up the location of the where the shader wants the texture coordinates
textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
And set up that attribute
gl.enableVertexAttribArray(textureCoordAttribute);
gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
Also while we're at it there's a bunch of minor things about the code you might want to consider
The code is turning on attributes in initShaders but if you have multiple shaders you'll need to turn on/off attributes in your draw call
The code is assigning properties to WebGL objects as in shaderProgram.pMatrixUniform = ... and squareVertexPositionBuffer.itemSize = 3; but if you ever decide to handle WebGLContextLost events that code will break because gl.createBuffer and gl.createShader will return null which means your code will be doing null.itemSize = 3 for example. It's better to make your own objects with WebGL as in var buffer = { id: gl.createBuffer(), itemSize: 3, ...}; so that if you do decide to handle WebGLContextLost events your code won't break.
The code is setting the attributes and uniforms for every face of the cube. But since they're the same for every face it would be more efficient to set the ones that don't change just once outside the loop.
The stuff about gl.viewportWidth and gl.viewportHeight is semi confusing since it makes it look like viewportWidth and viewportHeight are official parts of WebGL even though they are not. On top of that if you have a canvas that scales or changes shape they'll get out of sync which it does in resizeCanvas. Better just to use gl.canvas.width, gl.canvas.height directly or even better gl.drawBufferWidth, gl.drawingBufferHeight
The size the canvas is displayed is separate from its resolution. It's more appropriate to set your aspect ratio to the size it is displayed. In other words mat4.perspective(90, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.1, 3000.0, pMatrix); will make it render in the correct aspect ratio regardless of the resolution of the canvas.