Using mat4 attribute in WebGL2 - javascript

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>

Related

How do I optimize WebGL to render more objects quickly?

I am under the impression that WebGL is much more powerful than the 2d renderer in the browser but for some reason, my WebGL code runs much slower. Are there any recommended optimization strategies I can use to make my WebGL code run a little bit faster?
Are there any code in my rect function that should be left out because I am new to WebGL and most tutorials don't cover how to make a rect function.
WebGL Code
const canvas = document.getElementById("canvas");
const vertexCode = `
precision mediump float;
attribute vec4 position;
uniform mat4 matrix;
uniform vec4 color;
varying vec4 col;
void main() {
col = color;
gl_Position = matrix * position;
}
`;
const fragmentCode = `
precision mediump float;
varying vec4 col;
void main() {
gl_FragColor = col;
}
`;
const width = canvas.width;
const height = canvas.height;
const gl = canvas.getContext("webgl");
if(!gl) {
console.log("WebGL not supported");
}
const projectionMatrix = [
2/width, 0, 0, 0,
0, -2/height, 0, 0,
0, 0, 1, 0,
-1, 1, 0, 1
];
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexCode);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentCode);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
function rect(x, y, w, h) {
const vertex = [
x, y, 0, 1,
x+w, y, 0, 1,
x, y+h, 0, 1,
x+w, y+h, 0, 1
]
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertex), gl.STATIC_DRAW);
const positionLocation = gl.getAttribLocation(program, "position");
gl.enableVertexAttribArray(positionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 4, gl.FLOAT, false, 0, 0);
const projectionLocation = gl.getUniformLocation(program, `matrix`);
gl.uniformMatrix4fv(projectionLocation, false, projectionMatrix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function fill(r, g, b, a) {
const projectionLocation = gl.getUniformLocation(program, `color`);
gl.uniform4fv(projectionLocation, [r, g, b, a]);
}
let lastTime = new Date();
function animate() {
let currentTime = new Date();
console.log(1000 / (currentTime.getTime() - lastTime.getTime()));
lastTime = new Date();
requestAnimationFrame(animate);
for(let i=0;i<200;i++) {
fill(1, 0, 0, 1);
rect(random(0, 800), random(0, 600), 10, 10);
}
}
animate();
function random(low, high) {
return low + Math.random() * (high-low)
}
Using the normal 2D renderer
const canvas = document.getElementById("canvas");
const c = canvas.getContext("2d");
let lastTime = new Date();
function animate() {
let currentTime = new Date();
console.log(1000 / (currentTime.getTime() - lastTime.getTime()));
lastTime = new Date();
requestAnimationFrame(animate);
c.fillStyle = "black";
c.fillRect(0, 0, 800, 600);
c.fillStyle = "red";
for(let i=0;i<200;i++) {
c.fillRect(random(0, 800), random(0, 600), 10, 10);
}
}
animate();
function random(low, high) {
return low + Math.random() * (high-low)
}
There are 1000s of ways to optimized WebGL. Which one you choose depends on your needs. The more you optimize generally the less flexible.
First you should do the obvious and not create new buffers for every rectangle as your code is doing now and also move anything outside the rendering that can be moved outside (like looking up locations) which is shown in the answer by Józef Podlecki.
Another is you could move and scale the rectangle use the uniform matrix rather than uploading new vertices for each rectangle. It's unclear whether or not updating the matrix would be faster or slower for rectangles but if you were drawing something with more vertices it would definitely be faster do it by matrix. This series of articles mentions that and builds up to matrices.
Another is you can use vertex arrays though that's not important for your example since you're only drawing a single thing.
Another is if the shapes are all the same (as yours are) you can use instanced drawing to draw all 200 rectangles with one draw call
If the shapes are not all the same you can use creative techniques like storing their data in textures.
Another is you can put more than one shape in a buffer. So for example instead of putting one rectangle in the buffer put all 200 rectangles in the buffer and then draw them with one draw call. From this presentation
Also note: the callback to requestAnimationFrame is passed the time since the page loaded so there is no need to create Date objects. Creating Date objects is slower than not and the time passed to requestAnimationFrame is more accurate than Date
let lastTime = 0;
function animate(currentTime) {
console.log(1000 / (currentTime - lastTime));
lastTime = currentTime;
...
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
It's also slow to print to the console. You'd be better off updating an element's content
let lastTime = 0;
function animate(currentTime) {
someElement.textContent = (1000 / (currentTime - lastTime)).toFixed(1);
lastTime = currentTime;
...
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
You can reuse buffer, extract getAttribLocation,getUniformLocation outside of rect function as well as vertexAttribPointer once you bound buffer and lastly call requestAnimationFrame after rendering
const canvas = document.getElementById("canvas");
const vertexCode = `
precision mediump float;
attribute vec4 position;
uniform mat4 matrix;
uniform vec4 color;
varying vec4 col;
void main() {
col = color;
gl_Position = matrix * position;
}
`;
const fragmentCode = `
precision mediump float;
varying vec4 col;
void main() {
gl_FragColor = col;
}
`;
const width = canvas.width;
const height = canvas.height;
const gl = canvas.getContext("webgl");
if(!gl) {
console.log("WebGL not supported");
}
const projectionMatrix = [
2/width, 0, 0, 0,
0, -2/height, 0, 0,
0, 0, 1, 0,
-1, 1, 0, 1
];
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexCode);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentCode);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
const positionBuffer = gl.createBuffer();
const positionLocation = gl.getAttribLocation(program, "position");
const projectionLocation = gl.getUniformLocation(program, `matrix`);
const projectionColorLocation = gl.getUniformLocation(program, `color`);
gl.enableVertexAttribArray(positionLocation);
const vertex = [
0, 0, 0, 1,
0, 0, 0, 1,
0, 0, 0, 1,
0, 0, 0, 1
]
const floatArray = new Float32Array(vertex)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 4, gl.FLOAT, false, 0, 0);
gl.uniformMatrix4fv(projectionLocation, false, projectionMatrix);
function rect(x, y, w, h) {
floatArray[0] = x;
floatArray[1] = y;
floatArray[4] = x + w;
floatArray[5] = y;
floatArray[8] = x;
floatArray[9] = y + h;
floatArray[12] = x + w;
floatArray[13] = y + h;
gl.bufferData(gl.ARRAY_BUFFER, floatArray, gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function fill(r, g, b, a) {
gl.uniform4fv(projectionColorLocation, [r, g, b, a]);
}
let lastTime = new Date();
function animate() {
let currentTime = new Date();
console.log(1000 / (currentTime.getTime() - lastTime.getTime()));
lastTime = new Date();
for(let i=0;i<200;i++) {
fill(1, 0, 0, 1);
rect(random(0, 800), random(0, 600), 10, 10);
}
requestAnimationFrame(animate);
}
animate();
function random(low, high) {
return low + Math.random() * (high-low)
}
<canvas id="canvas" width="500" height="300"></canvas>

WebGL "draw a square" program, need help debugging it

I am following the LearningWebGL tutorials, I am not able to reproduce the first code itself. Here is the buggy code :
var modelView = mat4.create()
var projection = mat4.create()
var vertexPositionPointer, modelViewMatrixPointer, projectionMatrixPointer
var vertexBuffer
var vertices =
[
0.5, 0.5, 0.0,
-0.5, 0.5, 0.0,
0.5, -0.5, 0.0,
-0.5, -0.5, 0.0
]
var vertexShaderSource =
"attribute vec3 vertexPosition;"+ "\n" +
"uniform mat4 modelView;"+ "\n" +
"uniform mat4 projection;"+ "\n" +
"void main(){"+ "\n" +
"gl_Position = projection * modelView * vec4(vertexPosition, 1.0);"+ "\n" +
"}";
var fragmentShaderSource =
"void main(){"+ "\n" +
"gl_FragColor = vec4(1.0, 0, 0, 1.0);"+ "\n" +
"}";
function setup(){
var canvas = document.getElementById('canvas')
var gl = canvas.getContext("webgl", {antialias : false})
gl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight)
var vertexShader = getShader(gl, gl.VERTEX_SHADER, vertexShaderSource)
var fragmentShader = getShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource)
var program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
vertexPositionPointer = gl.getAttribLocation(program, "vertexPosition")
gl.enableVertexAttribArray(vertexPositionPointer)
modelViewMatrixPointer = gl.getUniformLocation(program, "modelView")
projectionMatrixPointer = gl.getUniformLocation(program, "projection")
vertexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
gl.vertexAttribPointer(vertexPositionPointer, 3, gl.FLOAT, gl.FALSE, 0, 0)
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
mat4.translate(modelView, modelView, [0, 0, -1.4])
mat4.perspective(projection, Math.PI/3.5, canvas.clientWidth/canvas.clientHeight, 1, 1000)
gl.uniformMatrix4fv(modelViewMatrixPointer, false, modelView)
gl.uniformMatrix4fv(projectionMatrixPointer, false, projection)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
}
function getShader(gl, type, source){
var shader = gl.createShader(type)
gl.shaderSource(shader, source)
gl.compileShader(shader)
console.log(gl.getShaderParameter(shader, gl.COMPILE_STATUS))
console.log(gl.getShaderInfoLog(shader))
return shader;
}
setup()
<script src="https://rawgit.com/toji/gl-matrix/master/dist/gl-matrix.js"></script>
<canvas id="canvas" style="border: none;"></canvas>
The above code produces :
Even though the z-translate value is set to -1, the entire canvas is painted red which too is incorrect.
What have I done wrong ?....
(some dummy text so that my word count increases and the post gets passed...)

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

Perspective projection showing nothing

I'm trying to follow WebGLFundamentals.org and LearningWebGL tutorials and I reached the projection part.
I create my scene something like LearningWebGL Tutorial 01 (with only the square):
var canvas;
var gl;
var shaderProgram;
// Vertex Shader
var positionLocation;
var uvMatrixLocation;
var pMatrixLocation;
var uvMatrix = mat4.create();
var pMatrix = mat4.create();
// Fragment Shader
var colorLocation;
var buffer = [];
function initGL() {
canvas = document.getElementById("webgl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
}
function createShader(gl, id, type) {
var shader;
var shaderSrc = document.getElementById(id);
if (type == "fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (type == "vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}
gl.shaderSource(shader, shaderSrc.text);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
function initShaders() {
var fragmentShader = createShader(gl, "fshader", "fragment");
var vertexShader = createShader(gl, "vshader", "vertex");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
// Linka os parametros do shader
positionLocation = gl.getAttribLocation(shaderProgram, "a_position");
uvMatrixLocation = gl.getUniformLocation(shaderProgram, "uvMatrix");
pMatrixLocation = gl.getUniformLocation(shaderProgram, "pMatrix");
gl.enableVertexAttribArray(positionLocation);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Não foi possível inicializar os shaders"); }
}
function initBuffers() {
createPoly([
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0
]);
}
function draw() {
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(shaderProgram);
mat4.perspective(pMatrix, Math.PI/3, 1, -10, 10);
gl.uniformMatrix4fv(pMatrixLocation, false, pMatrix);
gl.uniformMatrix4fv(uvMatrixLocation, false, uvMatrix);
buffer.forEach(function(e) {
gl.bindBuffer(gl.ARRAY_BUFFER, e.buffer);
gl.vertexAttribPointer(positionLocation, e.vertSize, gl.FLOAT, false, 0, 0);
gl.drawArrays(e.primtype, 0, e.nVerts());
});
}
window.onload = function() {
initGL();
initShaders();
initBuffers();
draw();
}
// ---------------------------------------------------------------
// --------------------------- Utils -----------------------------
// ---------------------------------------------------------------
function createPoly(vertices) {
var vertexBuffer;
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
var poly = {
buffer: vertexBuffer,
vertSize: 3,
nVerts: function() { return vertices.length/this.vertSize; },
primtype: gl.TRIANGLE_STRIP
};
buffer.push(poly);
}
<script src="https://www.khronos.org/registry/webgl/sdk/demos/common/webgl-utils.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>
<script id="vshader" type="x-shader/x-vertex">
attribute vec3 a_position;
uniform mat4 uvMatrix;
uniform mat4 pMatrix;
varying vec4 v_color;
void main() {
gl_Position = pMatrix * uvMatrix * vec4(a_position, 1);
v_color = gl_Position;
}
</script>
<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 v_color;
void main(void) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }
</script>
<canvas id="webgl-canvas" width="800" height="600"></canvas>
Then I set the projection on line 85:
Using orthogonal projection mat4.ortho(pMatrix, -5, 5, -5, 5, -5, 5); the square appears on my canvas
when I use perspective mat4.perspective(pMatrix, Math.PI/3, 1, -10, 10); it won't work
I've already tried several parameters
First off normally you'd make zNear and zFar positive numbers. They represent how the area in front of the camera that will be visible. Second is because your uvMatrix is the identity matrix your object as at the origin. The view is also at the origin (see cameras and perspective)
That means in order to view the object you either need to move the object away from the camera or add in a view matrix (which also effectively moves the object away from the origin)
I changed the code to this and it worked
// set zNear to 0.1
mat4.perspective(pMatrix, Math.PI/3, 1, 0.1, 10);
// move the object out from the camera
mat4.translate(uvMatrix, uvMatrix, [0, 0, -5]);
var canvas;
var gl;
var shaderProgram;
// Vertex Shader
var positionLocation;
var uvMatrixLocation;
var pMatrixLocation;
var uvMatrix = mat4.create();
var pMatrix = mat4.create();
// Fragment Shader
var colorLocation;
var buffer = [];
function initGL() {
canvas = document.getElementById("webgl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
}
function createShader(gl, id, type) {
var shader;
var shaderSrc = document.getElementById(id);
if (type == "fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (type == "vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}
gl.shaderSource(shader, shaderSrc.text);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
function initShaders() {
var fragmentShader = createShader(gl, "fshader", "fragment");
var vertexShader = createShader(gl, "vshader", "vertex");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
// Linka os parametros do shader
positionLocation = gl.getAttribLocation(shaderProgram, "a_position");
uvMatrixLocation = gl.getUniformLocation(shaderProgram, "uvMatrix");
pMatrixLocation = gl.getUniformLocation(shaderProgram, "pMatrix");
gl.enableVertexAttribArray(positionLocation);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert("Não foi possível inicializar os shaders"); }
}
function initBuffers() {
createPoly([
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0
]);
}
function draw() {
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(shaderProgram);
mat4.perspective(pMatrix, Math.PI/3, 1, 0.1, 10);
mat4.translate(uvMatrix, uvMatrix, [0, 0, -5]);
gl.uniformMatrix4fv(pMatrixLocation, false, pMatrix);
gl.uniformMatrix4fv(uvMatrixLocation, false, uvMatrix);
buffer.forEach(function(e) {
gl.bindBuffer(gl.ARRAY_BUFFER, e.buffer);
gl.vertexAttribPointer(positionLocation, e.vertSize, gl.FLOAT, false, 0, 0);
gl.drawArrays(e.primtype, 0, e.nVerts());
});
}
window.onload = function() {
initGL();
initShaders();
initBuffers();
draw();
}
// ---------------------------------------------------------------
// --------------------------- Utils -----------------------------
// ---------------------------------------------------------------
function createPoly(vertices) {
var vertexBuffer;
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
var poly = {
buffer: vertexBuffer,
vertSize: 3,
nVerts: function() { return vertices.length/this.vertSize; },
primtype: gl.TRIANGLE_STRIP
};
buffer.push(poly);
}
<script src="https://www.khronos.org/registry/webgl/sdk/demos/common/webgl-utils.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>
<script id="vshader" type="x-shader/x-vertex">
attribute vec3 a_position;
uniform mat4 uvMatrix;
uniform mat4 pMatrix;
varying vec4 v_color;
void main() {
gl_Position = pMatrix * uvMatrix * vec4(a_position, 1);
v_color = gl_Position;
}
</script>
<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 v_color;
void main(void) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }
</script>
<canvas id="webgl-canvas" width="800" height="600"></canvas>
Change zNear to a small, positive number.

Square doesn't appear using perspective matrix

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,
];

Categories

Resources