I am currently working on project in HTML and js where I have a simple canvas and two text fields (x and y) with a button called "Draw".
Trying to get it to draw a line via the user's entered x and y intercepts onto the canvas with onclick.
The function I am calling with onclick is generateImage().
I can tell that the text fields are in fact receiving the users info. I tested this by using a window.alert to show a pop up with the info (i have it commented out for now).
At first I thought it had to do with how i set up my button, but after asking around I think this cannot be true:
<button type="button" onclick="generateImage()"> DRAW LINE!</button>
I have no more errors on the console.
I even had my professor look at it and he said that it looked alright. I am beginning to think that this is a problem somewhere else within the program. I am sure I am either a) being stupid/frustrated or b) seriously over looking something.
Here is some of my code.
One last thing. var x0=... and var y0 =... I am aware that I will get a slope of zero and a line length of zero because they are the same..
My professor has told us to use this algorithm even thought it doesn't make sense to me. I would like to mention that I have also tried to hard code the x and y values and it still does not work.
Thanks
HTML
<body>
<form action="demo_form.asp">
X = <input name="X Value" type="text" id="Xvalue"><br>
Y = <input name="Y Value" type="text" id="Yvalue"><br>
</form>
<button type="button" onclick="generateImage()"> DRAW LINE!</button>
<p>Click "DRAW LINE" button to draw the line with custom X and Y coords</p>
<canvas id="gl-canvas" width="512", height="512">
<pre>No Support... :(</pre>
</canvas>
</body>
***JS***
function generateImage()
{
var x0 = document.getElementById('Xvalue').value;
var y0 = document.getElementById('Yvalue').value;
var x1 = document.getElementById('Xvalue').value;
var y1 = document.getElementById('Yvalue').value;
// window.alert(x0);
// window.alert(y0);
var slope = (y1-y0)/(x1-x0);
for (var i=0; i<x1-x0; i++){
var y = (slope*i)+y0;
var x = x0 + i;
myImage.setPixel(x,y, RED);
}
}
function glInit(canvas)
{
//makes our GL instance
gl=WebGLUtils.setupWebGL(canvas);
//gets and initializes our vertex and fragment shaders
program = initShaders(gl, "vertex-shader", "fragment-shader");
//sets the resulting shader program to use
gl.useProgram(program);
}
window.onload=function init()
{
var canvas = document.getElementById("gl-canvas");
glInit(canvas);
myImage=new Image(canvas);
myImage.setImageSize(700, 350);
myImage.clearImage();
generateImage(myImage);
renderImage(myImage);
}
function renderImage(myImage)
{
points=[];
colors=[];
for (var i=0; i<pixels.length; i++)
{
var y=Math.floor(i/myImage.width);
var x=i-(y*myImage.width);
colors.push(myImage.getPixel(x, y));
x/=myImage.width/2;
y/=myImage.height/2;
x-=1.0;
y-=1.0;
points.push(vec3(x, y, 0.0, 0.0));
}
gl.clearColor(0.7, 0.7, 0.7, 1.0);
gl.viewport(0, 0, myImage.width, myImage.height);
var posBufferID=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBufferID);
gl.bufferData(gl.ARRAY_BUFFER, flatten(points), gl.STATIC_DRAW);
program.vPositionA=gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(program.vPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(program.vPositionA);
var colBufferID=gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colBufferID);
gl.bufferData(gl.ARRAY_BUFFER, flatten(colors), gl.STATIC_DRAW);
program.vColorA=gl.getAttribLocation(program, "vColor");
gl.vertexAttribPointer(program.vColorA, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(program.vColorA);
render(program, colBufferID, posBufferID);
}
function render(program, colBufferID, posBufferID)
{
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, colBufferID);
gl.vertexAttribPointer(program.vColorA, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, posBufferID);
gl.vertexAttribPointer(program.vPositionA, 3, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.POINTS, 0, points.length);
}
Related
Three triangles are rotating clockwise around the y-axis. However, there are some "surface elimination" problems when the triangles overlap. All the time, only one triangle remains in front. I have OR ED, the DEPTH_BUFFER_BIT and depthMask enabled too, so the depth buffer is writable. But this does not solve my problem.
I am unable to paste the full project's code here, but I may provide the draw function below.
function draw() {
if (objects.length != 0) {
gl.clearColor(0, 0, 0, 1)
gl.depthMask(true)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
for (var index = 0; index < objects.length; index++) {
gl.uniformMatrix4fv(program.perspectivePointer, false, program.perspectiveMatrix)
gl.uniformMatrix4fv(program.worldTransformPointer, false, program.worldTransformMatrix)
gl.bindBuffer(gl.ARRAY_BUFFER, masterVertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(objects[index].getVertexList()), gl.STATIC_DRAW)
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(objects[index].getColorList()), gl.STATIC_DRAW)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3)
}
}
requestAnimationFrame(draw, program)
}
Please ask for the any other stuff, if needed.
I have also tried depth sorting, but no luck.
Completely new to javascript, attempting to do a webGL project.
Here is what I am working towards in the end, the idea is to have two for loops
// Create the vertex data for a grid to be drawn
function createGrid(gl, x, y) {
var vertexBuffer;
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
var matrix= []
for(var i = 0, i < x, ++i)
{
for(var j = 0, i < y, ++j)
{
matrix.push(createSquare(i,j))
}
}
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(matrix), gl.STATIC_DRAW);
var grid = {buffer:vertexBuffer, vertSize:3, nVerts:matrix.length, primtype:gl.TRIANGLE_STRIP};
return grid;
}
I am trying to dynamically create a wall of squares essentially. I have two functions.
// Create the vertex data for a square to be drawn
function createSquare(gl, x, y) {
var square = [ x+ .5, y+ .5, 0.0,
x+ -.5, y+ .5, 0.0,
x+ .5,y+ -.5, 0.0,
x+ -.5,y+ -.5, 0.0];
return square;
}
// Create the vertex data for a grid to be drawn
function createGrid(gl, x, y) {
var vertexBuffer;
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
var matrix= []
matrix.push(createSquare(gl,x,y ));
var numVerts = matrix.length;
alert(matrix[0]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(matrix), gl.STATIC_DRAW);
var grid = {buffer:vertexBuffer, vertSize:3, nVerts:numVerts, primtype:gl.TRIANGLE_STRIP};
return grid;
}
What is messing me up is the line
matrix.push(createSquare(gl,x,y ));
I was expecting to get matrixpopulated with the same array that was created in createSquare. However, all the the items in my square object are in the first index of matrix, assuming in string format. Searched and all I saw were people not having their array returned at all.
matrix[0] = 1.5,0.5,0,0.5,0.5,0,1.5,-0.5,0,0.5,-0.5,0 and
matrix[1] = undefined
EDIT: I get it now, matrix[0] contains the array object. This is also why I tried array.concat() earlier but then I was in the same situation as others where the array didn't return at all. array.push() was the only function that returned me anything.
Instead of pushing createSquare(gl,x,y) into grid just set grid equal to it so:
grid = createSquare(gl, x, y);
You are defining your grid variable as an array, and then pushing a square into it, as the way it is, grid will be always an array with length === 1, if You just replace:
grid.push(createSquare(gl,x,y ));
with:
grid = createSquare(gl,x,y);
It should work as you want.
Also note that you are redefining grid in the following line:
var grid = {buffer:vertexBuffer, vertSize:3, nVerts:numVerts, primtype:gl.TRIANGLE_STRIP};
Maybe thats intended, but I comment it to you so that you know.
So the answer was.....
matrix = matrix.concat(createSquare(x,y));
When I was goofing around earlier I was just using
matrix.concat(createSquare(x,y));
...and it didn't complain.
The issue in your original code is createSquare returns an array. So the line
matrix.push(createSquare(i,j))
is making an array of arrays. But new Float32Array(matrix) from the line
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(matrix), gl.STATIC_DRAW);
expects a single array, not an array of arrays. There are multiple ways to fix this. The simplest is to change this like
matrix.push(createSquare(i,j))
to this
matrix = matrix.concat(createSquare(i,j))
The concat function takes the array you have and the arrays you pass it and concatenates them into a new array. In other words, a single line matrix.concat(someArray) will do nothing because it will make a new array but because that array is not assigned to a variable it's lost immediately.
But note that is a slow solution. Maybe it's fast enough for your needs?
The reason it's slow is because every time you concat a bunch of work needs to be done. Every element from the previous 2 arrays needs to copied to a new array. So for example lets say you made a 1000 squares. The code would be doing
newArray = arrayOf0Elements + arrayOf12Elements;
newArray = arrayOf12Elements + arrayOf12Elements;
newArray = arrayOf24Elements + arrayOf12Elements;
newArray = arrayOf36Elements + arrayOf12Elements;
newArray = arrayOf48Elements + arrayOf12Elements;
...
newArray = arrayof11964Elements + arrayOf12Elements;
newArray = arrayof11976Elements + arrayOf12Elements;
newArray = arrayof11988Elements + arrayOf12Elements;
Hopefully you can see why that's slow. That's 6006000 copies that have to happen!
One simple way to make it fast-er is to concat at the end like this. Leave the code exactly as was but add this line
var combinedData = Array.prototype.concat.apply([], matrix);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(combinedData), gl.STATIC_DRAW);
This way, in the 1000 square case, you make 1000 squares of 12 elements each. You then concat them all at once. That's only 12000 copies vs the other way which was 6006000 copies.
Array.prototype.concat is the bare JavaScript function for concatting arrays. Calling apply lets you pass it an object to operate on, in this case [] and then the arguments to it in an array. It's the same as if you had done this
var combineData = [].concat(matrix[0], matrix[1], matrix[2], ... matrix[999]);
One of the fastest ways would be to create the Float32Array at the beginning and pass it into createSquare with an offset like this
// Create the vertex data for a square to be drawn
function createSquare(dst, offset, x, y) {
dst[offset++] = x+ .5;
dst[offset++] = y+ .5;
dst[offset++] = 0.0;
dst[offset++] = x+ -.5;
dst[offset++] = y+ .5;
dst[offset++] = 0.0;
dst[offset++] = x+ .5;
dst[offset++] = y+ -.5;
dst[offset++] = 0.0;
dst[offset++] = x+ -.5;
dst[offset++] = y+ -.5;
dst[offset++] = 0.0;
return offset;
}
// Create the vertex data for a grid to be drawn
function createGrid(gl, x, y) {
var vertexBuffer;
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// allocate space for all vertices up front
var matrix = new Float32Array(x * y * 12);
var offset = 0;
for(var i = 0, i < x, ++i)
{
for(var j = 0, i < y, ++j)
{
offset = createSquare(matrix, offset, i, j))
}
}
gl.bufferData(gl.ARRAY_BUFFER, matrix, gl.STATIC_DRAW);
var grid = {buffer:vertexBuffer, vertSize:3, nVerts:matrix.length, primtype:gl.TRIANGLE_STRIP};
return grid;
}
Now there's no concatenation at all and no extra arrays being created.
Which way you choose is up to you. I'd probably pick the middle concat version as it's probably the most flexible because you don't have to compute the number of vertices up front so if you change the number of vertices per square you don't have to change any other code.
I am trying to render a model under multiple light sources using multiple drawElement calls with different light sources. The gist of the code is as follows.
this.setUniform("u_matrix", "Matrix4fv", false, matrix);
this.setUniform("u_nMatrix", "Matrix3fv", false, nMatrix);
this.setUniform("u_kSpecular", "3fv", material.specular);
this.setUniform("u_kDiffuse", "3fv", material.diffuse);
this.setUniform("u_kAmbient", "3fv", material.ambient);
this.setUniform("u_shininess", "1f", material.shininess);
this.setVertexAttribArray("a_position", new Float32Array(mesh.vertices), 3);
this.setVertexAttribArray("a_normal", new Float32Array(mesh.vertexNormals), 3);
this.setElementArray(new Uint16Array(mesh.indices));
for(var i = 0;i < lights.length;i++) {
if(i == 1) {
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE);
gl.blendEquation(gl.FUNC_ADD);
}
var light = lights[i];
console.dir(light);
this.setUniform("u_lightType", "1i", light.type);
this.setUniform("u_lightVector", "3fv", light.vector);
this.setUniform("u_lightColor", "3fv", light.color);
gl.drawElements(gl.TRIANGLES, mesh.indices.length, gl.UNSIGNED_SHORT, 0);
}
gl.disable(gl.BLEND);
The issue here is that only the first lighting is rendered. Subsequent drawElements do not render anything. I do not think the issue is with my shader code, because the issue persists even if I strip the shader down to just set fragment color equal to the light type or some such parameter.
setUniform setVertexAttribArray and setElementArray are some helpers that i have written. They basically look like this:
setElementArray: function(array) {
var gl = this.gl;
var buffer = this._arrBuffers.__indexBuffer;
if(_.isUndefined(buffer)) {
buffer = gl.createBuffer();
this._arrBuffers.__indexBuffer = buffer;
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, array, gl.STATIC_DRAW);
}
setVertexAttribArray: function(name, array, size, type, normalized, stride, offset) {
var gl = this.gl;
size = size || 2;
type = type || gl.FLOAT;
normalized = normalized || false;
stride = stride || 0;
offset = offset || 0;
var buffer = this._arrBuffers[name];
if(_.isUndefined(buffer)) {
buffer = gl.createBuffer();
this._arrBuffers[name] = buffer;
}
var location = this.getLocation(name, true);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, size, type, normalized, stride, offset);
}
I have found the solution. I had set gl.depthFunc(gl.LESS), so subsequent renderings failed the depth test. Setting gl.depthFunc(gl.LEQUAL) solved the problem.
I'm trying to render a few objects to a canvas and I'm having a bit of trouble understanding what's not working.
I'm building two objects at the moment that represent the two meshes that I want to render. If I create one mesh the code works fine so the problem, I think, is that the data gets screwed up when I'm building two or more.
Here's an example of the mesh data:
"name":"cone",
"buffers":{
"vertexPosition":{}, // Buffer
"vertexIndex":{} // Buffer
},
"mesh":{
"vertices":[], // emptied it to fit on page
"faces":[] // emptied it to fit on page
},
"mvMatrix": Float32Array[16],
"itemSize":3,
"numItems":12,
"program":{
"vertexPosAttrib":0,
"mvMatrixUniform":{},
"pMatrixUniform":{}
}
This is build from this function:
buildMeshData: function(){
this.mvMatrix = mat4.create();
this.buffers.vertexPosition = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.vertexPosition);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(this.mesh.vertices), gl.STATIC_DRAW);
this.buffers.vertexIndex = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffers.vertexIndex);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.mesh.faces), gl.STATIC_DRAW);
this.itemSize = 3;
this.numItems = this.mesh.faces.length;
var vertexProps = {
attributes: ['vec3', 'VertexPosition'],
uniforms: ['mat4', 'MVMatrix', 'mat4', 'PMatrix'],
varyings: ['vec3', 'TexCoord']
}
var vertexShaderFunction = 'vTexCoord = aVertexPosition + 0.5; gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1);';
var vshaderInput = utils.buildVertexShader(vertexProps, vertexShaderFunction);
var fragmentProps = {
attributes: [],
uniforms: [],
varyings: ['vec3', 'TexCoord']
}
var fragmentShaderFunction = 'gl_FragColor = vec4(vTexCoord, 1);';
var fshaderInput = utils.buildFragmentShader(fragmentProps, fragmentShaderFunction);
this.program = gl.createProgram();
var vshader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vshader, vshaderInput);
gl.compileShader(vshader);
var fshader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fshader, fshaderInput);
gl.compileShader(fshader);
gl.attachShader(this.program, vshader);
gl.attachShader(this.program, fshader);
gl.linkProgram(this.program);
gl.useProgram(this.program);
this.program.vertexPosAttrib = gl.getAttribLocation(this.program, 'aVertexPosition');
gl.vertexAttribPointer(this.program.vertexPosAttrib, this.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.program.vertexPosAttrib);
this.program.mvMatrixUniform = gl.getUniformLocation(this.program, "uMVMatrix");
this.program.pMatrixUniform = gl.getUniformLocation(this.program, "uPMatrix");
scene.add(this);
}
and the render function goes like this:
function render(){
currentTime = new Date().getTime();
deltaTime = (currentTime - initialTime) / 1000; // in seconds
gl.viewport(0, 0, stage.width, stage.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
for(var i in scene.meshes){
(function(mesh){
mat4.translate(mesh.mvMatrix, mesh.mvMatrix, [0, 2 * i, -10 - (10 * i)]);
gl.useProgram(mesh.program);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.buffers.vertexIndex);
gl.vertexAttribPointer(mesh.program.vertexPosAttrib, mesh.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(mesh.program.vertexPosAttrib);
gl.uniformMatrix4fv(mesh.program.mvMatrixUniform, false, mesh.mvMatrix);
gl.uniformMatrix4fv(mesh.program.pMatrixUniform, false, scene.pMatrix);
gl.drawElements(gl.TRIANGLES, mesh.numItems, gl.UNSIGNED_SHORT, 0);
gl.disableVertexAttribArray(mesh.program.vertexPosAttrib);
})(scene.meshes[i])
}
// requestAnimationFrame(render);
}
The result of this is the second object is drawn correctly but the first causes the error:
[.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 0
...and is therefore not drawn.
Any ideas where the problem lies. Hopefully thats enough information from the code, I didn't want to put up too much, but if you need to see anything else I'll update.
This code
gl.vertexAttribPointer(this.program.vertexPosAttrib, this.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.program.vertexPosAttrib);
Need to be called when drawing each mesh and not where it's called now. Additionally before calling gl.vertexAttribPointer for this.program.vertexPosAttrib you need to call
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.vertexPosition);
Because gl.vertexAttribPointer binds the buffer currently bound to gl.ARRAY_BUFFER to the specified attribute.
In other words
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffers.vertexPosition);
gl.vertexAttribPointer(mesh.program.vertexPosAttrib, mesh.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(mesh.program.vertexPosAttrib);
I was trying to draw a simple square based on the book WebGL Beginner's guid by Diego Cantor & Brandon Jones but got stuck in lesson two.
I was trying to draw a square but my console shows error message as follows:
WebGL: INVALID_OPERATION: drawArrays: attribs not setup correctly
What i tried is given below:
My Code sample
<html>
<head>
<title>WebGL-Lesson One</title>
<style type="text/css">
canvas {
border: 2px dotted black;
}
</style>
<script id="code-js" type="text/javascript">
var gl = null;
function getGlContext() {
var my_canvas = document.getElementById("my_canvas");
var names = [ "webgl", "experimental-webgl", "webkit-3d", "moz-webgl" ];
for ( var i = 0; i < names.length; ++i) {
try {
gl = my_canvas.getContext(names[i]);
} catch (e) {
}
if (gl)
break;
}
if (gl == null) {
alert("empty canvas")
} else {
drawing();
}
}
function drawing() {
var vertices = [ -50.0, 50.0, 0.0, -50.0, -50.0, 0.0, 50.0, -50.0, 0.0,
50.0, 50.0, 0.0 ];
var indices = [ 3, 2, 1, 3, 1, 0 ]
var myVBOBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, myVBOBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices),
gl.STATIC_DRAW);
var aVertexPosition = (0,0,0);
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aVertexPosition);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
// gl.bindBuffer(gl.ARRAY_BUFFER, null);
var myIBOBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, myIBOBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices),gl.STATIC_DRAW);
// gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
</script>
<script id="shader-fs" type="x-shader/xfragment"></script>
<script id="shader-vs" type="x-shader/x-vertex"></script>
</head>
<body onload="getGlContext()">
<canvas id="my_canvas" width="700" height="500"></canvas>
</body>
</html>
Your shader scripts appear to be empty. You need at the very least a basic vertex and fragment shader. You also need to load, compile them and create the shader programs, and then you need to call gl.useProgram to set up your attribs, get uniform locations, and set uniform values. I recommend trying these lessons, they worked for me.