Square doesn't appear using perspective matrix - javascript

I have problem with getting started with WebGL. I don't understand why, but the reason why it's not working is the perspective matrix.
So there's the code that not working with perspective matrix:
<script type="x-shader/x-fragment" id="shader-fs">
void main (void) {
gl_FragColor = vec4(1,1,1,1);
}
</script>
<script type="x-shader/x-vertex" id="shader-vs">
attribute vec3 aVertexPosition;
uniform mat4 pMatrix;
uniform mat4 mvMatrix;
void main(void) {
gl_Position = pMatrix * mvMatrix * vec4(aVertexPosition, 1.0);
}
</script>
<script type="text/javascript">
function makePerspective(fovy, aspect, znear, zfar) {
var ymax = znear * Math.tan(fovy * Math.PI / 360.0);
var ymin = -ymax;
var xmin = ymin * aspect;
var xmax = ymax * aspect;
return makeFrustum(xmin, xmax, ymin, ymax, znear, zfar);
}
function makeFrustum(left, right,
bottom, top,
znear, zfar) {
var X = 2*znear/(right-left);
var Y = 2*znear/(top-bottom);
var A = (right+left)/(right-left);
var B = (top+bottom)/(top-bottom);
var C = -(zfar+znear)/(zfar-znear);
var D = -2*zfar*znear/(zfar-znear);
return [X, 0, A, 0,
0, Y, B, 0,
0, 0, C, D,
0, 0, -1, 0];
}
function identity() {
return [
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
];
}
var canvas, gl;
var win = {w: 0,h: 0};
var shaderProgram, vertexPositionAttribute;
var horizAspect = 480/640;
var squareVerticesBuffer;
function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
theSource = "";
currentChild = shaderScript.firstChild;
while (currentChild) {
if (currentChild.nodeType == currentChild.TEXT_NODE) {
theSource += currentChild.textContent;
}
currentChild = currentChild.nextSibling;
}
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,theSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader,gl.COMPILE_STATUS)) {
alert("An error compiling the shader "+gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
function initShaders() {
var fragmentShader = getShader(gl,"shader-fs");
var vertexShader = getShader(gl,"shader-vs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram,fragmentShader);
gl.attachShader(shaderProgram,vertexShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram,gl.LINK_STATUS)) {
alert("Cannot init shaders!");
}
gl.useProgram(shaderProgram);
vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(vertexPositionAttribute);
}
function initBuffers() {
squareVerticesBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
var vertices = [
1,1,0,
-1,1,0,
1,-1,0,
-1,-1,0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),gl.STATIC_DRAW);
}
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesBuffer);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
var perspectiveMatrix = makePerspective(45,640/480,0.1,100);
/* if you set up the line above ^ to: [
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
] it will work
*/
var pUniform = gl.getUniformLocation(shaderProgram, "pMatrix");
gl.uniformMatrix4fv(pUniform, false, new Float32Array(perspectiveMatrix));
var mvMatrix = identity();
var mvUniform = gl.getUniformLocation(shaderProgram, "mvMatrix");
gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix));
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function load() {
canvas = document.getElementById("c");
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
win.w = canvas.width; win.h = canvas.height;
if (gl) { // Ok!
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
initShaders();
initBuffers();
drawScene();
}
else { // Fallback to 2d!
gl = canvas.getContext("2d");
gl.font = "14px Arial";
gl.textAlign = "center";
gl.textBaseline = "bottom";
gl.fillStyle = "#000";
gl.fillText("You're browser doesn't support WebGL!",win.w/2,win.h/2);
}
}
window.onload = load;
</script>
I took code from this example. But I don't want to use Sylvester or glMatrix.

I've found the solution to my problem! I got wrong the idea of creating matrix arrays, each next 4 elements are columns not rows. So the translation matrix would be:
var arr = [
1,0,0,0,
0,1,0,0,
0,0,1,0,
vx,vy,vz,1,
];
not like this:
var arr = [
1,0,0,vx,
0,1,0,vy,
0,0,1,vz,
0,0,0,1,
];

Related

Using mat4 attribute in WebGL2

I am trying to pass a 4x4 matrix as an attribute to WebGL2 vertex shader. After following closely this SO answer, I am stuck with wrapping my head around why I can't successfully render and get a glDrawArrays: attempt to access out of range vertices in attribute 0 error in my console.
It is my understanding that mat4 is represented as 4 times vec4 in WebGL. When I query a_modelMatrix position on the GPU I can see it occupies location from 0 to 3, so this seems okay. I break up the assignments into 4 parts like this:
for (let i = 0; i < 4; ++i) {
gl.enableVertexAttribArray(a_modelMatrix + i)
gl.vertexAttribPointer(a_modelMatrix + i, 4, gl.FLOAT, false, 64, i * 16)
}
But still get an error.
Here is my code:
console.clear()
const canvas = document.createElement('canvas')
const gl = canvas.getContext('webgl')
document.body.appendChild(canvas)
canvas.width = 500
canvas.height = 500
gl.viewport(0, 0, 500, 500)
const vertexShaderSrc = `
attribute mat4 a_modelMatrix;
attribute vec4 a_pos;
void main () {
gl_Position = a_modelMatrix * a_pos;
}
`
const fragmentShaderSrc = `
precision highp float;
void main () {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
`
const vShader = makeShader(gl.VERTEX_SHADER, vertexShaderSrc)
const fShader = makeShader(gl.FRAGMENT_SHADER, fragmentShaderSrc)
const program = makeProgram(vShader, fShader)
gl.useProgram(program)
const modelMatrix = mat4.create()
const scale = vec3.create()
vec3.set(scale, 2, 2, 2)
mat4.scale(modelMatrix, modelMatrix, scale)
const a_pos = gl.getAttribLocation(program, 'a_pos')
const a_modelMatrix = gl.getAttribLocation(program, 'a_modelMatrix')
const posBuffer = gl.createBuffer()
const modelMatrixBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -0.1, 0.1, 0.1, 0.1, 0.0, 0.0 ]), gl.STATIC_DRAW)
gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(a_pos)
gl.bindBuffer(gl.ARRAY_BUFFER, modelMatrixBuffer)
gl.bufferData(gl.ARRAY_BUFFER, modelMatrix, gl.STATIC_DRAW)
for (let i = 0; i < 4; ++i) {
gl.enableVertexAttribArray(a_modelMatrix + i)
gl.vertexAttribPointer(a_modelMatrix + i, 4, gl.FLOAT, false, 64, i * 16)
}
gl.drawArrays(gl.LINE_LOOP, 0, 3)
function makeShader (type, src) {
const shader = gl.createShader(type)
gl.shaderSource(shader, src)
gl.compileShader(shader)
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
return shader
}
console.log(gl.getShaderInfoLog(shader))
}
function makeProgram (vShader, fShader) {
const program = gl.createProgram()
gl.attachShader(program, vShader)
gl.attachShader(program, fShader)
gl.linkProgram(program)
if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
return program
}
console.log(gl.getProgramInfoLog(shader))
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
You must specify 1 attribute for each vertex coordinate. The vertices cannot share 1 matrix attribute. Each vertex coordinate must have its own model matrix attribute:
at_array = new Float32Array(16*no_of_vertices)
for (let i = 0; i < no_of_vertices; i ++)
mat_array.set(modelMatrix, 16*i)
gl.bufferData(gl.ARRAY_BUFFER, mat_array, gl.STATIC_DRAW)
console.clear()
const canvas = document.createElement('canvas')
const gl = canvas.getContext('webgl')
document.body.appendChild(canvas)
canvas.width = 300
canvas.height = 300
gl.viewport(0, 0, 300, 300)
const vertexShaderSrc = `
attribute mat4 a_modelMatrix;
attribute vec4 a_pos;
void main () {
gl_Position = a_modelMatrix * a_pos;
}
`
const fragmentShaderSrc = `
precision highp float;
void main () {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
`
const vShader = makeShader(gl.VERTEX_SHADER, vertexShaderSrc)
const fShader = makeShader(gl.FRAGMENT_SHADER, fragmentShaderSrc)
const program = makeProgram(vShader, fShader)
gl.useProgram(program)
const modelMatrix = mat4.create()
const scale = vec3.create()
vec3.set(scale, 2, 2, 2)
mat4.scale(modelMatrix, modelMatrix, scale)
const a_pos = gl.getAttribLocation(program, 'a_pos')
const a_modelMatrix = gl.getAttribLocation(program, 'a_modelMatrix')
const posBuffer = gl.createBuffer()
const modelMatrixBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -0.3, 0.3, 0.3, 0.3, 0.0, 0.0 ]), gl.STATIC_DRAW)
gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(a_pos)
gl.bindBuffer(gl.ARRAY_BUFFER, modelMatrixBuffer)
let mat_array = new Float32Array(16*3)
for (let i = 0; i < 3; i ++) mat_array.set(modelMatrix, 16*i)
gl.bufferData(gl.ARRAY_BUFFER, mat_array, gl.STATIC_DRAW)
for (let i = 0; i < 4; ++i) {
gl.enableVertexAttribArray(a_modelMatrix + i)
gl.vertexAttribPointer(a_modelMatrix + i, 4, gl.FLOAT, false, 64, i * 16)
}
gl.drawArrays(gl.LINE_LOOP, 0, 3)
function makeShader (type, src) {
const shader = gl.createShader(type)
gl.shaderSource(shader, src)
gl.compileShader(shader)
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
return shader
}
console.log(gl.getShaderInfoLog(shader))
}
function makeProgram (vShader, fShader) {
const program = gl.createProgram()
gl.attachShader(program, vShader)
gl.attachShader(program, fShader)
gl.linkProgram(program)
if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
return program
}
console.log(gl.getProgramInfoLog(shader))
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
If you use WebGL 1.0, I recommend to use a Uniform variable instead of the matrix attribute. A uniform is a global Shader variable:
attribute vec4 a_pos;
uniform mat4 u_modelMatrix;
void main () {
gl_Position = u_modelMatrix * a_pos;
}
const u_modelMatrix = gl.getUniformLocation(program, 'u_modelMatrix')
// [...]
gl.uniformMatrix4fv(u_modelMatrix, false, modelMatrix)
console.clear()
const canvas = document.createElement('canvas')
const gl = canvas.getContext('webgl')
document.body.appendChild(canvas)
canvas.width = 300
canvas.height = 300
gl.viewport(0, 0, 300, 300)
const vertexShaderSrc = `
attribute vec4 a_pos;
uniform mat4 u_modelMatrix;
void main () {
gl_Position = u_modelMatrix * a_pos;
}
`
const fragmentShaderSrc = `
precision highp float;
void main () {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
`
const vShader = makeShader(gl.VERTEX_SHADER, vertexShaderSrc)
const fShader = makeShader(gl.FRAGMENT_SHADER, fragmentShaderSrc)
const program = makeProgram(vShader, fShader)
gl.useProgram(program)
const modelMatrix = mat4.create()
const scale = vec3.create()
vec3.set(scale, 2, 2, 2)
mat4.scale(modelMatrix, modelMatrix, scale)
const a_pos = gl.getAttribLocation(program, 'a_pos')
const u_modelMatrix = gl.getUniformLocation(program, 'u_modelMatrix')
const posBuffer = gl.createBuffer()
const modelMatrixBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -0.3, 0.3, 0.3, 0.3, 0.0, 0.0 ]), gl.STATIC_DRAW)
gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(a_pos)
gl.uniformMatrix4fv(u_modelMatrix, false, modelMatrix)
gl.drawArrays(gl.LINE_LOOP, 0, 3)
function makeShader (type, src) {
const shader = gl.createShader(type)
gl.shaderSource(shader, src)
gl.compileShader(shader)
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
return shader
}
console.log(gl.getShaderInfoLog(shader))
}
function makeProgram (vShader, fShader) {
const program = gl.createProgram()
gl.attachShader(program, vShader)
gl.attachShader(program, fShader)
gl.linkProgram(program)
if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
return program
}
console.log(gl.getProgramInfoLog(shader))
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>
However, If you create a WebGL 2.0 context:
const gl = canvas.getContext('webgl')
const gl = canvas.getContext('webgl2')
and a Vertex Array Object:
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
You can use Instancing. The matix attribute is an instance attribute:
gl.bindBuffer(gl.ARRAY_BUFFER, modelMatrixBuffer)
gl.bufferData(gl.ARRAY_BUFFER, modelMatrix, gl.STATIC_DRAW)
for (let i = 0; i < 4; ++i) {
gl.enableVertexAttribArray(a_modelMatrix + i)
gl.vertexAttribPointer(a_modelMatrix + i, 4, gl.FLOAT, false, 64, i * 16)
gl.vertexAttribDivisor(a_modelMatrix + i, 1)
}
gl.drawArrays(gl.LINE_LOOP, 0, 3)
gl.drawArraysInstanced(gl.LINE_LOOP, 0, 3, 1)
console.clear()
const canvas = document.createElement('canvas')
const gl = canvas.getContext('webgl2')
document.body.appendChild(canvas)
canvas.width = 300
canvas.height = 300
gl.viewport(0, 0, 300, 300)
const vertexShaderSrc = `
attribute mat4 a_modelMatrix;
attribute vec4 a_pos;
void main () {
gl_Position = a_modelMatrix * a_pos;
}
`
const fragmentShaderSrc = `
precision highp float;
void main () {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
`
const vShader = makeShader(gl.VERTEX_SHADER, vertexShaderSrc)
const fShader = makeShader(gl.FRAGMENT_SHADER, fragmentShaderSrc)
const program = makeProgram(vShader, fShader)
gl.useProgram(program)
const modelMatrix = mat4.create()
const scale = vec3.create()
vec3.set(scale, 2, 2, 2)
mat4.scale(modelMatrix, modelMatrix, scale)
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const a_pos = gl.getAttribLocation(program, 'a_pos')
const a_modelMatrix = gl.getAttribLocation(program, 'a_modelMatrix')
const posBuffer = gl.createBuffer()
const modelMatrixBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ -0.3, 0.3, 0.3, 0.3, 0.0, 0.0 ]), gl.STATIC_DRAW)
gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(a_pos)
gl.bindBuffer(gl.ARRAY_BUFFER, modelMatrixBuffer)
gl.bufferData(gl.ARRAY_BUFFER, modelMatrix, gl.STATIC_DRAW)
for (let i = 0; i < 4; ++i) {
gl.enableVertexAttribArray(a_modelMatrix + i)
gl.vertexAttribPointer(a_modelMatrix + i, 4, gl.FLOAT, false, 64, i * 16)
gl.vertexAttribDivisor(a_modelMatrix + i, 1)
}
//gl.drawArrays(gl.LINE_LOOP, 0, 3)
gl.drawArraysInstanced(gl.LINE_LOOP, 0, 3, 1)
function makeShader (type, src) {
const shader = gl.createShader(type)
gl.shaderSource(shader, src)
gl.compileShader(shader)
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
return shader
}
console.log(gl.getShaderInfoLog(shader))
}
function makeProgram (vShader, fShader) {
const program = gl.createProgram()
gl.attachShader(program, vShader)
gl.attachShader(program, fShader)
gl.linkProgram(program)
if (gl.getProgramParameter(program, gl.LINK_STATUS)) {
return program
}
console.log(gl.getProgramInfoLog(shader))
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"></script>

Write a function that will plot x/y for a sine wave to be drawn by WebGL?

I'm trying to learn WebGL (and some math from codingmath). The goal today is to draw a sine wave at any start and ending direction.
Something like this:
I'm just missing something in my makePoints() method. My points plot out oddly and I'm kinda dumbfounded on where to go next.
QUESTION:
How do I fix my makePoints() function, so that it will plot out the x and y coords of a sine wave.
let gl,
shaderProgram,
vertices,
canvas;
const VERTEX_LENGTH = 1500;
const VERTEX_SHADER = `
attribute vec4 coords;
attribute float pointSize;
void main(void) {
gl_Position = coords;
gl_PointSize = pointSize;
}
`;
const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 color;
void main(void) {
gl_FragColor = color;
}
`;
initGL();
createShader();
createVertices();
draw();
window.addEventListener('resize', setCanvasSize, false);
function setCanvasSize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}
function initGL() {
canvas = document.querySelector('#canvas');
gl = canvas.getContext('webgl');
setCanvasSize();
console.log(gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 1);
}
function makePoints(points) {
const diff = (Math.PI * 2) / (points - 1);
const len = {length: points};
return Array.from(len, (_, i) => Math.sin(i * diff));
}
function createVertices() {
vertices = makePoints(VERTEX_LENGTH);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);
const coords = gl.getAttribLocation(shaderProgram, 'coords');
gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coords);
// gl.bindBuffer(gl.ARRAY_BUFFER, null);
const pointSize = gl.getAttribLocation(shaderProgram, 'pointSize');
gl.vertexAttrib1f(pointSize, 2);
const uniformColor = gl.getUniformLocation(shaderProgram, 'color');
gl.uniform4f(uniformColor, 0, normalize(200), normalize(83), 1);
}
function createShader() {
const vs = VERTEX_SHADER;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vs);
gl.compileShader(vertexShader);
const fs = FRAGMENT_SHADER;
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fs);
gl.compileShader(fragmentShader);
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
}
function draw() {
console.log(vertices)
gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, VERTEX_LENGTH/2);
requestAnimationFrame(draw);
}
function normalize(val, max=255, min=0) { return (val - min) / (max - min); }
html, body, canvas {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
display: block;
position: relative;
}
<canvas id="canvas" width="500" height="500"></canvas>
Since your code expects a 2 points per vertex you need your makePoints to return different values for even (x) and odd (y) values.
I find that it's much easier to understand verbose code so here's my makePoints. Note that I find it useful to always compute a lerp0to1 value in the loop like this. I can then use that value to easily convert to nearly any type of data I want.
function makePoints(points) {
const highestPointNdx = points / 2 - 1;
return Array.from({length: points}, (_, i) => {
const pointId = i / 2 | 0;
const lerp0To1 = pointId / highestPointNdx;
const odd = i % 2;
return odd
? Math.sin(lerp0To1 * Math.PI * 2) // Y
: (lerp0To1 * 2 - 1); // X
});
}
let gl,
shaderProgram,
vertices,
canvas;
const VERTEX_LENGTH = 1500;
const VERTEX_SHADER = `
attribute vec4 coords;
attribute float pointSize;
void main(void) {
gl_Position = coords;
gl_PointSize = pointSize;
}
`;
const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 color;
void main(void) {
gl_FragColor = color;
}
`;
initGL();
createShader();
createVertices();
draw();
window.addEventListener('resize', setCanvasSize, false);
function setCanvasSize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}
function initGL() {
canvas = document.querySelector('#canvas');
gl = canvas.getContext('webgl');
setCanvasSize();
console.log(gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 1);
}
function makePoints(points) {
const highestPointNdx = points / 2 - 1;
return Array.from({length: points}, (_, i) => {
const pointId = i / 2 | 0;
const lerp0To1 = pointId / highestPointNdx;
const odd = i % 2;
return odd
? Math.sin(lerp0To1 * Math.PI * 2) // Y
: (lerp0To1 * 2 - 1); // X
});
}
function createVertices() {
vertices = makePoints(VERTEX_LENGTH);
console.log(vertices);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);
const coords = gl.getAttribLocation(shaderProgram, 'coords');
gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coords);
// gl.bindBuffer(gl.ARRAY_BUFFER, null);
const pointSize = gl.getAttribLocation(shaderProgram, 'pointSize');
gl.vertexAttrib1f(pointSize, 2);
const uniformColor = gl.getUniformLocation(shaderProgram, 'color');
gl.uniform4f(uniformColor, 0, normalize(200), normalize(83), 1);
}
function createShader() {
const vs = VERTEX_SHADER;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vs);
gl.compileShader(vertexShader);
const fs = FRAGMENT_SHADER;
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fs);
gl.compileShader(fragmentShader);
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
}
function draw() {
gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, VERTEX_LENGTH/2);
requestAnimationFrame(draw);
}
function normalize(val, max=255, min=0) { return (val - min) / (max - min); }
html, body, canvas {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
display: block;
position: relative;
}
<canvas id="canvas" width="500" height="500"></canvas>
Let me add I think makePoints is currently a little confusing. I'd change it to take the number of points you want, not the number of values in the vertex buffer (which is what it takes now) which is different from the number of points. You if you want N points you need 2*N values. So, I'd change it to
function makePoints(numPoints) {
const highestPointNdx = numPoints - 1;
return Array.from({length: numPoints * 2}, (_, i) => {
const pointId = i / 2 | 0;
const lerp0To1 = pointId / highestPointNdx;
const isY = i % 2;
return isY
? Math.sin(lerp0To1 * Math.PI * 2) // Y
: (lerp0To1 * 2 - 1); // X
});
}
Then I pass in VERTEX_LENGTH and I use the same value for gl.drawArrays and neither would have to change if I was using 3D points instead of 2D points.
let gl,
shaderProgram,
vertices,
canvas;
const VERTEX_LENGTH = 1500;
const VERTEX_SHADER = `
attribute vec4 coords;
attribute float pointSize;
void main(void) {
gl_Position = coords;
gl_PointSize = pointSize;
}
`;
const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 color;
void main(void) {
gl_FragColor = color;
}
`;
initGL();
createShader();
createVertices();
draw();
window.addEventListener('resize', setCanvasSize, false);
function setCanvasSize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}
function initGL() {
canvas = document.querySelector('#canvas');
gl = canvas.getContext('webgl');
setCanvasSize();
console.log(gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 1);
}
function makePoints(numPoints) {
const highestPointNdx = numPoints - 1;
return Array.from({length: numPoints * 2}, (_, i) => {
const pointId = i / 2 | 0;
const lerp0To1 = pointId / highestPointNdx;
const isY = i % 2;
return isY
? Math.sin(lerp0To1 * Math.PI * 2) // Y
: (lerp0To1 * 2 - 1); // X
});
}
function createVertices() {
vertices = makePoints(VERTEX_LENGTH);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);
const coords = gl.getAttribLocation(shaderProgram, 'coords');
gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coords);
// gl.bindBuffer(gl.ARRAY_BUFFER, null);
const pointSize = gl.getAttribLocation(shaderProgram, 'pointSize');
gl.vertexAttrib1f(pointSize, 2);
const uniformColor = gl.getUniformLocation(shaderProgram, 'color');
gl.uniform4f(uniformColor, 0, normalize(200), normalize(83), 1);
}
function createShader() {
const vs = VERTEX_SHADER;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vs);
gl.compileShader(vertexShader);
const fs = FRAGMENT_SHADER;
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fs);
gl.compileShader(fragmentShader);
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
}
function draw() {
gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, VERTEX_LENGTH);
requestAnimationFrame(draw);
}
function normalize(val, max=255, min=0) { return (val - min) / (max - min); }
html, body, canvas {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
display: block;
position: relative;
}
<canvas id="canvas" width="500" height="500"></canvas>
The vertex buffer you provided, wasn't big enough. It should store 2 floats for x AND y (instead of 1)
I've rewritten it: (check the makePoints2)
let gl,
shaderProgram,
vertices,
canvas;
const VERTEX_LENGTH = 1500;
const VERTEX_SHADER = `
attribute vec4 coords;
attribute float pointSize;
void main(void) {
gl_Position = coords;
gl_PointSize = pointSize;
}
`;
const FRAGMENT_SHADER = `
precision mediump float;
uniform vec4 color;
void main(void) {
gl_FragColor = color;
}
`;
initGL();
createShader();
createVertices();
draw();
window.addEventListener('resize', setCanvasSize, false);
function setCanvasSize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
}
function initGL() {
canvas = document.querySelector('#canvas');
gl = canvas.getContext('webgl');
setCanvasSize();
console.log(gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0, 0, 0, 1);
}
function makePoints(points) {
const diff = (Math.PI * 2) / (points - 1);
const len = {length: points};
return Array.from(len, (_, i) => Math.sin(i * diff));
}
function makePoints2(points) {
let arr = Array(points * 2);
let index = 0;
for(var i=0;i<points;i++) {
let val = (i/points) * (Math.PI * 2); // lerp 0..points => 0..2PI
arr[index] = ((i/points)*2)-1; // x, lerp 0..points => -1..1 range
arr[index+1] = Math.sin(val); // y, the sinus function...
index += 2; // next vertex
}
return arr;
}
function createVertices() {
// Feel like my function is close but I'm missing something
vertices = makePoints2(VERTEX_LENGTH);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.DYNAMIC_DRAW);
const coords = gl.getAttribLocation(shaderProgram, 'coords');
gl.enableVertexAttribArray(coords);
gl.vertexAttribPointer(coords, 2, gl.FLOAT, false, 0, 0);
const pointSize = gl.getAttribLocation(shaderProgram, 'pointSize');
gl.vertexAttrib1f(pointSize, 2);
const uniformColor = gl.getUniformLocation(shaderProgram, 'color');
gl.uniform4f(uniformColor, 0, normalize(200), normalize(83), 1);
}
function createShader() {
const vs = VERTEX_SHADER;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vs);
gl.compileShader(vertexShader);
const fs = FRAGMENT_SHADER;
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fs);
gl.compileShader(fragmentShader);
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
}
function draw() {
console.log(vertices)
//gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Float32Array(vertices));
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, VERTEX_LENGTH);
//requestAnimationFrame(draw);
}
function normalize(val, max=255, min=0) { return (val - min) / (max - min); }
html, body, canvas {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
display: block;
position: relative;
}
<canvas id="canvas" width="500" height="500"></canvas>
Thanks gman about how to do the snippets

Webgl does not draw array

I am trying to draw square grid on canvas.The array is filled with vertices. But the page remains blank. There seems to be no error. Thank you
var canvas;
var gl;
var grid = [];
var maxNumTriangles = 200;
var maxNumVertices = 3 * maxNumTriangles;
var index = 0;
window.onload = function init() {
canvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
if (!gl) { alert("WebGL isn't available"); }
canvas.addEventListener("mousedown", function (event) {
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gridarray();
document.write(grid[0]);
gl.bufferSubData(gl.ARRAY_BUFFER, 8 * index, flatten(grid));
});
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
var program = initShaders(gl, "vertex-shader", "fragment-shader");
gl.useProgram(program);
var vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, 8 * maxNumVertices, gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
var cBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cBuffer);
//gl.bufferData(gl.ARRAY_BUFFER, 16 * maxNumVertices, gl.STATIC_DRAW);
render();
}
function gridarray() {
p = 10;
for (var x = 0; x <= 500; x += 40) {
var g = new Float32Array([0.5 + x + p, p]);
var g1 = new Float32Array([0.5 + x + p, 500 + p]);
grid.push(g);
grid.push(g1);
}
for (var x = 0; x <= 500; x += 40) {
var g = new Float32Array([p, 0.5 + x + p]);
var g1 = new Float32Array([500 + p, 0.5 + x + p]);
grid.push(g);
grid.push(g1);
}
}
function render() {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.LINES, 0, 20);
window.requestAnimFrame(render);
}
Fragment and Vertex shader
attribute vec4 vPosition;
void
main()
{
gl_Position = vPosition;
}
void main()
{
gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}
you should post executable code. Without seeing the rest of the code it's hard to tell what's wrong.
Of the top of my head
What does flatten return?
I know of no flatten that takes an array of Float32Arrays and
and returns a Float32Array but I guess your's does?
It looks like you're passing in pixel coordinates but
WebGL requires clip space coordinates.

WebGL draw loop and translation

I'm want my javascript program to be shifting a triangle to the right.
What I'm focusing on right now are matrices and draw loops. I've never done such a thing so I may be off road but what I'm attempting to do, for study purposes is this:
-Setup the webgl pipeline so I display correctly a triangle (OK)
-Write a function with a matrix that allows me to pass in the values of translation (seems ok but I'm not sure)
var translation_prototype = [1,0,0,0,
0,1,0,0,
0,0,1,0,
tx,ty,0,1];
Leaving out for the moment rotation a scaling and modelview, since I'm perfectly happy(just for the sake of an exercise) with the orthographic view webgl provides.
-Setup a loop that cycles through the drawArrays (even here I'm not sure if the loop starts and ends in the correct place)
I suspect I'm really close, but the triangle doesn't move (tx remains constant).
Here's the code(I think I don't even need to clear the color and depth buffer since I'm only translating on x axis)
<!DOCTYPE HTML>
<html>
<canvas id = "can" width="400" height="400">
</canvas>
<script>
var webgl_canvas = document.getElementById('can');
var gl = webgl_canvas.getContext('experimental-webgl');
var triangles = [-0.8,-0.8,0,0.8,-0.8,0,0,0.8,0];
var vertexBuffer = gl.createBuffer();
var tx = 0.1;
var ty = 0;
var translation_prototype = [1,0,0,0,
0,1,0,0,
0,0,1,0,
tx,ty,0,1];
var vertexShader_source = 'attribute vec3 a_position;' + 'uniform vec4 u_translation;' + 'void main() { gl_Position = u_translation * vec4 (a_position,1);}';
var fragmentShader_source = 'precision mediump float;' + 'void main() { gl_FragColor = vec4 (0.9,0,0.1,1); }';
function getTimeInSeconds () {
return Date.now() * 0.001;
}
function makeTranslation (tx, ty) {
return translation_prototype;
}
//Compile shaders
var buildShader = function (shaderSource, typeOfShader) {
var shader = gl.createShader(typeOfShader);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert (gl.getShaderInfoLog(shader));
}
return shader;
}
var compiledVertexShader = buildShader (vertexShader_source, gl.VERTEX_SHADER);
var compiledFragmentShader = buildShader (fragmentShader_source, gl.FRAGMENT_SHADER);
//setup GLSL program
program = gl.createProgram();
gl.attachShader(program,compiledVertexShader);
gl.attachShader(program,compiledFragmentShader);
gl.linkProgram(program);
//Fill the buffer with vertex data
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(triangles), gl.STATIC_DRAW);
vertexBuffer.itemSize = 3;
vertexBuffer.numItems = 3;
gl.clear(gl.COLOR_BUFFER_BIT);
var positionLocation = gl.getAttribLocation(program,"a_position");
gl.enableVertexAttribArray(positionLocation);
gl.useProgram(program);
var shaderTranlsationMatrix = gl.getUniformLocation(program, "u_translation");
gl.uniformMatrix4fv(shaderTranlsationMatrix,false,new Float32Array(translation_prototype));
gl.vertexAttribPointer(positionLocation, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
var startTime = 0;
function animate (time) {
//Draw loop
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
var deltaTime = (time - startTime);
makeTranslation((tx*deltaTime),(ty*deltaTime));
console.log(tx,ty,deltaTime);
gl.drawArrays (gl.TRIANGLES, 0, vertexBuffer.numItems);
startTime = time;
window.requestAnimationFrame(animate);
}
animate(0);
</script>
</html>
<!-- start last edited snippet -->
<!DOCTYPE HTML>
<html>
<canvas id = "can" width="400" height="400">
</canvas>
<script>
var webgl_canvas = document.getElementById('can');
var gl = webgl_canvas.getContext('experimental-webgl');
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,0,1,-1,0,0,1,0]), gl.STATIC_DRAW);
vertexBuffer.itemSize = 3;
vertexBuffer.numItems = 3;
var identityMatrix = [1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1];
function translation (tx,ty,tz) {
return [1,0,0,0,
0,1,0,0,
0,0,1,0,
tx,ty,tz,1]
}
var vertexShader_source = 'attribute vec3 a_position;' + 'uniform mat4 u_move;' + 'void main() { gl_Position = u_move * vec4 (a_position,1); }';
var fragmentShader_source = 'precision mediump float;' + 'void main() { gl_FragColor = vec4 (0.9,0,0.1,1); }';
//Compile shaders
var buildShader = function (shaderSource, typeOfShader) {
var shader = gl.createShader(typeOfShader);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert (gl.getShaderInfoLog(shader));
}
return shader;
}
var compiledVertexShader = buildShader (vertexShader_source, gl.VERTEX_SHADER);
var compiledFragmentShader = buildShader (fragmentShader_source, gl.FRAGMENT_SHADER);
//setup GLSL program
program = gl.createProgram();
gl.attachShader(program,compiledVertexShader);
gl.attachShader(program,compiledFragmentShader);
gl.linkProgram(program);
var positionLocation = gl.getAttribLocation(program,"a_position");
gl.enableVertexAttribArray(positionLocation);
gl.useProgram(program);
var tx = 0, ty = 0, tz = 0;
var translate = gl.getUniformLocation (program, "u_move");
gl.uniformMatrix4fv(translate,false,new Float32Array(identityMatrix));
gl.vertexAttribPointer(positionLocation, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
//Draw
var start_time =0;
var animate=function(time) {
var dt= time-start_time;
tx+=0.5;
translation((dt*tx),0,0);
console.log(dt);
console.log(tx);
start_time=time;
gl.drawArrays (gl.TRIANGLES, 0, vertexBuffer.numItems);
window.requestAnimationFrame(animate);
}
animate(0);
</script>
</html>
<!-- end last edited snippet -->
Here is the working snippet
JSFIDDLE
Your vertex shader should look like this:
attribute vec3 a_position;' + 'uniform mat4 u_translation;' + 'void main() { gl_Position = u_translation*vec4 (a_position,1);}
In order to make objects move in space you have to multiply all your vectors and matrices with the position vector to get the result. Tranlation Wiki
You have to update your translation_prototype variable every loop cycle:
deltaTime += 0.005;
makeTranslation(tx+deltaTime,ty+deltaTime);
the deltaTime was declared outside the loop and incremented every cycle
Also your makeTranslation function should look like this:
function makeTranslation (x, y) {
translation_prototype =
[1,0,0,0,
0,1,0,0,
0,0,1,0,
x,y,0,1]
return translation_prototype;
}
(you can get rid of the return statement if you are using global variables, though is recommended to use local variables)
(I had to try this new snippet feature :D)
var webgl_canvas = document.getElementById('can');
var gl = webgl_canvas.getContext('experimental-webgl');
var triangles = [-0.5,-0.5,0,0.5,-0.5,0,0,0.5,0];
var vertexBuffer = gl.createBuffer();
var tx = 0;
var ty = 0;
var translation_prototype = [1,0,0,0,
- 0,1,0,0,
0,0,1,0,
0,0,0,1];
var vertexShader_source = 'attribute vec3 a_position;' + 'uniform mat4 u_translation;' + 'void main() { gl_Position = u_translation*vec4 (a_position,1);}';
var fragmentShader_source = 'precision mediump float;' + 'void main() { gl_FragColor = vec4 (0.9,0,0.1,1); }';
function getTimeInSeconds () {
return Date.now() * 0.001;
}
function makeTranslation (x, y) {
translation_prototype =
[1,0,0,0,
0,1,0,0,
0,0,1,0,
x,y,0,1]
return translation_prototype;
}
//Compile shaders
var buildShader = function (shaderSource, typeOfShader) {
var shader = gl.createShader(typeOfShader);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert (gl.getShaderInfoLog(shader));
}
return shader;
}
var compiledVertexShader = buildShader (vertexShader_source, gl.VERTEX_SHADER);
var compiledFragmentShader = buildShader (fragmentShader_source, gl.FRAGMENT_SHADER);
//setup GLSL program
program = gl.createProgram();
gl.attachShader(program,compiledVertexShader);
gl.attachShader(program,compiledFragmentShader);
gl.linkProgram(program);
//Fill the buffer with vertex data
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(triangles), gl.STATIC_DRAW);
vertexBuffer.itemSize = 3;
vertexBuffer.numItems = 3;
gl.clear(gl.COLOR_BUFFER_BIT);
var positionLocation = gl.getAttribLocation(program,"a_position");
gl.enableVertexAttribArray(positionLocation);
gl.useProgram(program);
var shaderTranlsationMatrix = gl.getUniformLocation(program, "u_translation");
gl.uniformMatrix4fv(shaderTranlsationMatrix,false,new Float32Array(translation_prototype));
gl.vertexAttribPointer(positionLocation, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
var startTime = 0;
var deltaTime = 0;
function animate (time) {
//Draw loop
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
deltaTime += 0.005;
makeTranslation(tx+deltaTime,ty+deltaTime);
gl.useProgram(program);
var shaderTranlsationMatrix = gl.getUniformLocation(program, "u_translation");
gl.uniformMatrix4fv(shaderTranlsationMatrix,false,new Float32Array(translation_prototype));
gl.vertexAttribPointer(positionLocation, vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.drawArrays (gl.TRIANGLES, 0, vertexBuffer.numItems);
startTime = time;
window.requestAnimationFrame(animate);
}
animate(0);
<canvas id = "can" width="300" height="300">
</canvas>

Texture not mapping to square correctly WebGL

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.

Categories

Resources