Related
I'm studying webgl.
Now I spray the food to a random location on the canvas, and when the mouse pointer and the food collide, I try to delete the food.
(The collision implementation of the mouse cursor is another issue, but it is not necessary now.)
However, no matter how many times I looked for it, I could not find a way to erase a specific object even if I explained how to draw it. Is there a way to delete only certain objects from canvas without a library?
The full text of my code is as follows.
var gl;
var points;
window.onload = function init()
{
var canvas = document.getElementById( "gl-canvas" );
gl = WebGLUtils.setupWebGL( canvas );
if ( !gl ) { alert( "WebGL isn't available" ); }
// Four Vertices
var vertices = [
vec2( -0.5, -0.5 ),
vec2( -0.5, 0.5 ),
vec2( 0.5, 0.5 ),
vec2( 0.5, -0.5)
];
//
// Configure WebGL
//
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
// Load shaders and initialize attribute buffers
var program = initShaders( gl, "vertex-shader", "fragment-shader" );
gl.useProgram( program );
// Load the data into the GPU
var bufferId = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, bufferId );
//gl.bufferData( gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW );
// Associate out shader variables with our data buffer
var foodX, foodY;
var foodSize = 20;
var foodNumber = 50;
var vPosition = gl.getAttribLocation( program, "vPosition" );
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer( vPosition, size, type, normalize, stride, offset);
gl.enableVertexAttribArray( vPosition );
// we added a uniform called vResolution.
var vResolution = gl.getUniformLocation(program, "vResolution");
var fColor = gl.getUniformLocation(program, "fColor");
// set the resolution
gl.uniform2f(vResolution, gl.canvas.width, gl.canvas.height);
// draw 50 random rectangles in random colors
while (foodNumber > 0) {
// Setup a random rectangle
// This will write to positionBuffer because
// its the last thing we bound on the ARRAY_BUFFER
// bind point
//food 좌표는 canvas width와 height 사이에 있도록 하며, canvas 밖으로 빠져나가지 않도록 조절한다.
foodX = randomInt(canvas.width - foodSize);
foodY = randomInt(canvas.height-foodSize);
setRectangle(gl, foodX, foodY, foodSize, foodSize);
foodNumber = foodNumber - 1;
// Set a random color.
gl.uniform4f(fColor, Math.random(), Math.random(), Math.random(), 1);
// Draw the rectangle.
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
}
};
// Returns a random integer from 0 to range - 1.
function randomInt(range) {
return Math.floor(Math.random() * range);
}
// Fills the buffer with the values that define a rectangle.
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
// NOTE: gl.bufferData(gl.ARRAY_BUFFER, ...) will affect
// whatever buffer is bound to the `ARRAY_BUFFER` bind point
// but so far we only have one buffer. If we had more than one
// buffer we'd want to bind that buffer to `ARRAY_BUFFER` first.
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2]), gl.STATIC_DRAW);
}
function pop(bufferName){
gl.deleteBuffer(bufferName)
}
<!DOCTYPE html>
<html>
<head>
<script id="vertex-shader" type="x-shader/x-vertex">
//attribute vec4 vPosition;
attribute vec2 vPosition;
uniform vec2 vResolution;
void
main()
{
// convert the position from pixels to 0.0 to 1.0
vec2 zeroToOne = vPosition / vResolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clip space)
vec2 clipSpace = zeroToTwo - 1.0;
//gl_Position = vec4(clipSpace, 0.0, 1.0);
// To get it to be the more traditional top left corner used for 2d graphics APIs we can just flip the clip space y coordinate.
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 fColor;
void
main()
{
gl_FragColor = fColor;
}
</script>
<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="snakeGame.js"></script>
</head>
<body>
<canvas id="gl-canvas" width="1024" height="800">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
<script>
var canvas =
</script>
</body>
</html>
There is no such thing as "deleting an object" at the webgl. WebGL is just an API that draws pixels into a canvas. "Objects" are a higher level concept that your code deals with.
You generally keep your own list of things to draw (whether that is the same as your list of objects or not is up to you)
Every "frame" you clear the canvas and then draw all things you want to draw
render function:
clear the canvas
for each thing to draw
draw thing
So "deleting" an object is a matter of not drawing it.
See this and this and this
As an example
const thingsToDraw = [
{ color: [1, 0, 0, 1], center: [0.2, 0.3], },
{ color: [0, 1, 0, 1], center: [0.0, 0.1], },
{ color: [0, 0, 1, 1], center: [-0.5, -0.4], },
{ color: [1, 0.5, 0, 1], center: [-0.2, 0.3], },
{ color: [0, 1, 1, 1], center: [0.7, -0.1], },
{ color: [1, 0, 1, 1], center: [-0.5, 0.4], },
];
const gl = document.querySelector('canvas').getContext('webgl');
const prg = twgl.createProgram(gl, [`
uniform vec4 position;
void main() {
gl_PointSize = 20.0;
gl_Position = position;
}`,`
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}
`]);
const positionLoc = gl.getUniformLocation(prg, "position");
const colorLoc = gl.getUniformLocation(prg, "color");
function drawThing(color, position) {
gl.useProgram(prg);
gl.uniform4f(positionLoc, ...position, 0, 1);
gl.uniform4fv(colorLoc, color);
gl.drawArrays(gl.POINTS, 0, 1); // draw 1 point
}
function render(time) {
time *= 0.001;
gl.clear(gl.COLOR_BUFFER_BIT);
thingsToDraw.forEach((thing, i) => {
const t = time * 10 + i;
const position = [
thing.center[0] + Math.cos(t) * 0.1,
thing.center[1] + Math.sin(t) * 0.1,
];
drawThing(thing.color, position);
});
requestAnimationFrame(render);
}
requestAnimationFrame(render);
document.querySelector('button').addEventListener('click', () => {
thingsToDraw.splice(0, 1);
});
canvas { border: 1px solid black; }
<canvas></canvas>
<button type="button">remove first thing</button>
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
How you decide to track and organize your "objects" or your "things to draw" is entirely up to you. Many 3D systems use a scene graph and then draw the entire graph every frame so 2 ways of not drawing something is to either remove it from the graph or else add some flag to each node whether or not to draw it.
In other systems the scene graph is separate from the list of things to draw.
For small programs people might just use an array (like the example above)
How to make 3DSierpinskiTriangle Spin. I have provided with html and JavaScript code below. You need to add this common folder and it .js files to make it work. It would be very help you anyone can solve this.
....................................................................................................................................................
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>3D Sierpinski Gasket</title>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec3 vPosition;
attribute vec3 vColor;
varying vec4 color;
void
main()
{
gl_Position = vec4(vPosition, 1.0);
color = vec4(vColor, 1.0);
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 color;
void
main()
{
gl_FragColor = color;
}
</script>
<script type="text/javascript" src="Common/webgl-utils.js"></script>
<script type="text/javascript" src="Common/initShaders.js"></script>
<script type="text/javascript" src="Common/MV.js"></script>
<script type="text/javascript" src="3DSierpinskiTriangle.js"></script>
</head>
<body>
<canvas id="gl-canvas" width="512" height="512">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
</html>
"use strict";
var canvas;
var gl;
var points = [];
var colors = [];
var NumTimesToSubdivide = 3;
window.onload = function init() {
canvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
if (!gl) { alert("WebGL isn't available"); }
//
// Initialize our data for the Sierpinski Gasket
//
// First, initialize the vertices of our 3D gasket
// Four vertices on unit circle
// Intial tetrahedron with equal length sides
var vertices = [
vec3(0.0000, 0.0000, -1.0000),
vec3(0.0000, 0.9428, 0.3333),
vec3(-0.8165, -0.4714, 0.3333),
vec3(0.8165, -0.4714, 0.3333)
];
divideTetra(vertices[0], vertices[1], vertices[2], vertices[3],
NumTimesToSubdivide);
//
// Configure WebGL
//
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
// enable hidden-surface removal
gl.enable(gl.DEPTH_TEST);
// Load shaders and initialize attribute buffers
var program = initShaders(gl, "vertex-shader", "fragment-shader");
gl.useProgram(program);
// Create a buffer object, initialize it, and associate it with the
// associated attribute variable in our vertex shader
var cBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cBuffer);
gl.bufferData(gl.ARRAY_BUFFER, flatten(colors), gl.STATIC_DRAW);
var vColor = gl.getAttribLocation(program, "vColor");
gl.vertexAttribPointer(vColor, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vColor);
var vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, flatten(points), gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
render();
};
function triangle(a, b, c, color) {
// add colors and vertices for one triangle
var baseColors = [
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
vec3(0.0, 0.0, 1.0),
vec3(0.0, 0.0, 0.0)
];
colors.push(baseColors[color]);
points.push(a);
colors.push(baseColors[color]);
points.push(b);
colors.push(baseColors[color]);
points.push(c);
}
function tetra(a, b, c, d) {
// tetrahedron with each side using
// a different color
triangle(a, c, b, 0);
triangle(a, c, d, 1);
triangle(a, b, d, 2);
triangle(b, c, d, 3);
}
function divideTetra(a, b, c, d, count) {
// check for end of recursion
if (count === 0) {
tetra(a, b, c, d);
}
// find midpoints of sides
// divide four smaller tetrahedra
else {
var ab = mix(a, b, 0.5);
var ac = mix(a, c, 0.5);
var ad = mix(a, d, 0.5);
var bc = mix(b, c, 0.5);
var bd = mix(b, d, 0.5);
var cd = mix(c, d, 0.5);
--count;
divideTetra(a, ab, ac, ad, count);
divideTetra(ab, b, bc, bd, count);
divideTetra(ac, bc, c, cd, count);
divideTetra(ad, bd, cd, d, count);
}
}
function render() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length);
}
function setup() { createCanvas(windowWidth, windowHeight); background(0); } function draw() { fill("#0002") noStroke() rect(0,0,width,height) recursivePyramid() } function dot(x, y) { this.x = x this.y = y this.plot = function() { fill("white") rect(this.x-1, this.y-1, 2, 2) } } function lineDot(from, to) { stroke("#fff5") strokeWeight(2) line(from.x,from.y, to.x, to.y) } function pyramid(x, y, rot, size) { down = [] down[0] = new dot(2,0) down[1] = new dot(10,1) down[2] = new dot(50,2) up = new dot(250,3) middle = new dot(31250,4) middle.plot() this.center = new dot(6250,5) this.center.plot() y = y + size/3*1 down[0].x = sin(2/3 * PI + rot) * size + x down[0].y = cos(2/3 * PI + rot) * size/4 + y down[1].x = sin(2 * 2/3 * PI + rot) * size + x down[1].y = cos(2 * 2/3 * PI + rot) * size/4 + y down[2].x = sin(3 * 2/3 * PI + rot) * size + x down[2].y = cos(3 * 2/3 * PI + rot) * size/4 + y up.x = x up.y = y - size down[0].plot() down[1].plot() down[2].plot() up.plot() lineDot(down[0], up) lineDot(down[1], up) lineDot(down[2], up) lineDot(down[1], down[0]) lineDot(down[2], down[1]) lineDot(down[0], down[2]) } function recursivePyramid(x = width/2, y = height/2, rot = frameCount/70, size = 250) { if (size > 10) { pyramid(x, y, rot, size) recursivePyramid(x, y - size/2, rot*2, size/2) recursivePyramid(x + size, y + size/2, rot+size*sin(rot/10)/99, size/2) recursivePyramid(x - size, y + size/2, rot+size*sin(rot/10)/99, size/2) } }
Im trying to render simple shapes ( circles, rectangles and triangles , however, they become very pixelated when WebGL Renders them.
Shader code:
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
uniform vec2 u_resolution;
void main() {
// convert the rectangle points from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
</script>
Here is my code for rendering the circle:
var WebGLRenderer = (function () {
function WebGLRenderer() {
this.canvas = document.getElementById('canvas')
this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl')
if (!this.gl) {
throw Error('Your browser does not support WebGL')
return
}
// Programs
this.rectangleProgram = webglUtils.createProgramFromScripts(this.gl, ['2d-vertex-shader', '2d-fragment-shader'])
// Locations
this.rectanglePoisitionLocation = this.gl.getAttribLocation(this.rectangleProgram, 'a_position')
// Uniforms
this.rectangleResolutionLocation = this.gl.getUniformLocation(this.rectangleProgram, 'u_resolution')
this.rectangleColorLocation = this.gl.getUniformLocation(this.rectangleProgram, 'u_color')
// this.positionBuffer = this.gl.createBuffer()
this.rectanglePositionBuffer = this.gl.createBuffer()
// this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)
requestAnimationFrame(this.render.bind(this))
}
WebGLRenderer.prototype.clearCanvas = function (color) {
var rgba = color.getColor()
this.gl.clearColor(...rgba)
this.gl.clear(this.gl.COLOR_BUFFER_BIT)
}
WebGLRenderer.prototype.drawCircle = function (x, y, radius, color) {
// Render circle
// For now user rectangleProgram
this.gl.useProgram(this.rectangleProgram)
this.gl.enableVertexAttribArray(this.rectanglePoisitionLocation)
// this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)
this.circleBuffer = this.gl.createBuffer()
// this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.rectanglePositionBuffer)
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.circleBuffer)
// Setup circle
var circleVertices = [x, y]
var numFans = 360
var anglePerFan = (2 * Math.PI) / numFans
for (var i = 0; i <= numFans; i++) {
var angle = anglePerFan * (i + 1)
var angledX = x + Math.cos(angle) * radius
var angledY = y + Math.sin(angle) * radius
circleVertices.push(angledX, angledY)
// circleVertices.push()
}
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(circleVertices), this.gl.DYNAMIC_DRAW)
// this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW)
var size = 2
var type = this.gl.FLOAT
var normalize = false
var stride = 0
var offset = 0
this.gl.vertexAttribPointer(this.rectanglePoisitionLocation, size, type, normalize, stride, offset)
this.gl.uniform2f(this.rectangleResolutionLocation, this.gl.canvas.width, this.gl.canvas.height)
// Color
var colorArray = color.getColor()
this.gl.uniform4fv(this.rectangleColorLocation, colorArray)
// Draw rectangle
var primitiveType = this.gl.TRIANGLE_FAN
// var primitiveType = this.gl.POINTS
var offset = 0
var count = circleVertices.length / size
// var count = positions.length / size
this.gl.drawArrays(primitiveType, offset, count)
}
WebGLRenderer.prototype.render = function (time) {
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height)
var delta = Math.sin(time / 1000) * 10
this.clearCanvas(new Color(0, 0, 0, 255))
var rectangleColor = new Color(0, 65, 255, 255)
var width = 50
var height = 50
var circleColor = new Color(0, 167, 255, 255)
this.drawCircle(10, 10, 10, circleColor)
requestAnimationFrame(this.render.bind(this))
}
return WebGLRenderer
})()
function Color(r, g, b, a) {
this.r = r
this.g = g
this.b = b
this.a = a
this.getColor = function () {
return [r / 255, g / 255, b / 255, a / 255]
}
}
var renderer = new WebGLRenderer()
Results: blurry circle (everything I render with WebGL is blurry)
See fiddle for results: https://jsfiddle.net/xLwmngav/1/
Expected results: a smooth round circle
Any help is appreciated. Thank you in advance.
As is pointed out in this article canvases have 2 sizes, their resolution (how many pixels are in them) and the size they are displayed.
Generally you want the resolution to match or exceed the size the canvas is displayed. The best way to do that is to to check, just before rendering, if the canvas's resolution matches the size it's displayed and if it's not to resize it with a function like this
function resize(canvas) {
// Lookup the size the browser is displaying the canvas.
const desiredWidth = canvas.clientWidth;
const desiredHeight = canvas.clientHeight;
// Check if the canvas is not the same size.
if (canvas.width !== desiredWidth ||
canvas.height !== desiredHeight) {
// Make the canvas the same size
canvas.width = desiredWidth;
canvas.height = desiredHeight;
}
}
And use it like this
function render() {
resize(canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
... draw here ...
...
example:
function resize(canvas) {
// Lookup the size the browser is displaying the canvas.
const desiredWidth = canvas.clientWidth;
const desiredHeight = canvas.clientHeight;
// Check if the canvas is not the same size.
if (canvas.width !== desiredWidth ||
canvas.height !== desiredHeight) {
// Make the canvas the same size
canvas.width = desiredWidth;
canvas.height = desiredHeight;
}
}
var WebGLRenderer = (function () {
function WebGLRenderer() {
this.canvas = document.getElementById('canvas')
this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl')
if (!this.gl) {
throw Error('Your browser does not support WebGL')
return
}
// Programs
this.rectangleProgram = webglUtils.createProgramFromScripts(this.gl, ['2d-vertex-shader', '2d-fragment-shader'])
// Locations
this.rectanglePoisitionLocation = this.gl.getAttribLocation(this.rectangleProgram, 'a_position')
// Uniforms
this.rectangleResolutionLocation = this.gl.getUniformLocation(this.rectangleProgram, 'u_resolution')
this.rectangleColorLocation = this.gl.getUniformLocation(this.rectangleProgram, 'u_color')
// this.positionBuffer = this.gl.createBuffer()
this.rectanglePositionBuffer = this.gl.createBuffer()
// this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)
requestAnimationFrame(this.render.bind(this))
}
WebGLRenderer.prototype.clearCanvas = function (color) {
var rgba = color.getColor()
this.gl.clearColor(...rgba)
this.gl.clear(this.gl.COLOR_BUFFER_BIT)
}
WebGLRenderer.prototype.drawCircle = function (x, y, radius, color) {
// Render circle
// For now user rectangleProgram
this.gl.useProgram(this.rectangleProgram)
this.gl.enableVertexAttribArray(this.rectanglePoisitionLocation)
// this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer)
this.circleBuffer = this.gl.createBuffer()
// this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.rectanglePositionBuffer)
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.circleBuffer)
// Setup circle
var circleVertices = [x, y]
var numFans = 360
var anglePerFan = (2 * Math.PI) / numFans
for (var i = 0; i <= numFans; i++) {
var angle = anglePerFan * (i + 1)
var angledX = x + Math.cos(angle) * radius
var angledY = y + Math.sin(angle) * radius
circleVertices.push(angledX, angledY)
// circleVertices.push()
}
/*var circleVertices = [
x, y,
15, 18,
5, 18,
0, 10,
4, 1,
14, 1,
20, 9,
15, 18
]*/
// three 2d points
// TODO: Research static draw
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(circleVertices), this.gl.DYNAMIC_DRAW)
// this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW)
var size = 2
var type = this.gl.FLOAT
var normalize = false
var stride = 0
var offset = 0
this.gl.vertexAttribPointer(this.rectanglePoisitionLocation, size, type, normalize, stride, offset)
this.gl.uniform2f(this.rectangleResolutionLocation, this.gl.canvas.width, this.gl.canvas.height)
// Color
var colorArray = color.getColor()
this.gl.uniform4fv(this.rectangleColorLocation, colorArray)
// Draw rectangle
var primitiveType = this.gl.TRIANGLE_FAN
// var primitiveType = this.gl.POINTS
var offset = 0
var count = circleVertices.length / size
// var count = positions.length / size
this.gl.drawArrays(primitiveType, offset, count)
}
WebGLRenderer.prototype.render = function (time) {
resize(this.gl.canvas);
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height)
var delta = Math.sin(time / 1000) * 10
this.clearCanvas(new Color(0, 0, 0, 255))
var rectangleColor = new Color(0, 65, 255, 255)
var width = 50
var height = 50
var circleColor = new Color(0, 167, 255, 255)
this.drawCircle(10, 10, 10, circleColor)
requestAnimationFrame(this.render.bind(this))
}
return WebGLRenderer
})()
function Color(r, g, b, a) {
this.r = r
this.g = g
this.b = b
this.a = a
this.getColor = function () {
return [r / 255, g / 255, b / 255, a / 255]
}
}
var renderer = new WebGLRenderer()
window.WebGLRenderer = WebGLRenderer
body {
margin: 0;
}
#canvas {
display: block; /* prevents scrollbar */
width: 100vw;
height: 100vh;
}
<canvas id="canvas"></canvas>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
uniform vec2 u_resolution;
void main() {
// convert the rectangle points from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
</script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/m3.js"></script>
The canvas defaults to a width and height of 300x150 pixels. These are attributes of the <canvas> element, not CSS properties. By scaling to 100vw by 100vh, you're just stretching those 300x150 pixels out to fill the screen.
To actually get a 1:1 mapping from canvas pixels to screen pixels, you need to set the width and height of the canvas to the size of the window:
this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
You may also want to listen for the resize event on window and update the canvas size accordingly.
Fiddle: https://jsfiddle.net/kL1a2zpr/
For some reason I cannot access the values from user input into the text fields I set up in my HTML document, when I use document.getElementbyId().value in my JS file. I've checked everything and can't find the issue. Could someone direct me to the right solution?
UPDATE: Here is a clearer explanation of the problem and output:
I am trying to make a customizable (by color and # of triangles), triangle fan. When I try to access any values inputted I believe they return null, because I cannot get a numeric value to use for the fan's RGB, thus I just get a black square as output (the background canvas).
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Rendering (colorful)</title>
<body>
<p> Number of Triangles <input id="numTris" type="number"></p>
<p>
R1 <input id="R1" type="number"/>
G1 <input id="G1" type="number"/>
B1 <input id="B1" type="number">
</p>
<p>
R2 <input id="R2" type="number"/>
G2 <input id="G2" type="number"/>
B2 <input id="B2" type="number"/>
</p>
<p>
<input type="button" onclick="solid(); start()" value="Solid?">
<input type="button" onclick="interp(); start()" value="Interpolated?">
</p>
<script type="text/javascript" src="js/rendering-filled-circle.js"></script>
</body>
<script type="text/javascript">
// WebGL rendering context
var colorType = false;
var size = height;
var gl = null;
function solid(){
colorType = true;
}
function interp(){
colorType = false;
}
function draw() {
renderTriangle(gl);
}
function start() {
var canvas = document.getElementById("canvas");
// Initialize the OpenGL rendering context
gl = canvas.getContext("experimental-webgl");
// Only continue if WebGL is available and working
if (gl) {
// initialize shader programs
initShaders(gl);
// initialize a very simple scene, a triangle
initBuffers(gl, colorType, size);
// call the draw() function every 20 milliseconds
setInterval(draw, 20);
}
else {
alert("WebGL initialization failed! Your browser does not support WebGL or it is not properly configured.");
}
}
</script>
</head>
<body
<center>
<canvas id="canvas" width="800" height="800">
If you are seeing this message your web browser does not support the HTML5 <canvas>> element.
</canvas>
</center>
</body>
</html>
Let me know if you have any questions!
Thank you
UPDATE: Here is the JS which features the .value usage and yes I know I do not use the RGB values yet.
var shaderProgram = null;
var vertexBuffer = null;
var vertexColorBuffer = null;
var aPositionIndex = -1;
var aVertexColor = -1;
var r1 = document.getElementById("R1").value;
var g1 = document.getElementById("G1").value;
var b1 = document.getElementById("B1").value;
var r2 = document.getElementById("R2").value;
var g2 = document.getElementById("G2").value;
var b2 = document.getElementById("B2").value;
var numTriangles = document.getElementById("numTris").value;
var numSides = numTriangles * numTriangles;
var numVertices = numTriangles * 3.0;
var PI2 = 2.0 * 3.1415926535897932384626433832795;
///// Initialize the data buffer to pass to the rendering pipeline
///// the geometry and its attributes.
function colorConverter(val){
if(val < 0){
val = 0;
}
if(val > 255){
val = 255;
}
val = val/255.0;
return val;
}
function initBuffers(gl, colorType, size) {
var coloring = colorType;
r1 = colorConverter(r1);
g1 = colorConverter(g1);
b1 = colorConverter(b1);
r2 = colorConverter(r2);
g2 = colorConverter(g2);
b2 = colorConverter(b2);
var radius = size/2;
var xVal = 0.0;
var yVal = 1.0;
triangleVertices = new Float32Array(numVertices * 2);
triangleVertices[0] = xVal;
triangleVertices[1] = yVal;
for(a = 2; a < numVertices*2; a++){
triangleVertices[a] = xVal + (radius * Math.cos(a * PI2 / numSides));
a += 1;
triangleVertices[a] = yVal + (radius * Math.sin(a * PI2 / numSides));
}
triangleVerticesColor = new Float32Array(numVertices*3);
for(a = 0; a < numVertices*3; a++){
if(coloring == true){
var v = 1.0;
}
else{
v = 0.5;
}
triangleVerticesColor[a] = v;
}
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, triangleVertices, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
vertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, triangleVerticesColor, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
///// Define and compile a very simple shader.
function initShaders(gl) {
var vertexShaderSource = "\
attribute vec3 a_position; \n\
attribute vec3 a_color; \n\
varying vec3 vertexcolor; \n\
void main(void) \n\
{ \n\
vertexcolor = a_color; \n\
gl_Position = vec4(a_position, 1.0); \n\
} \n\
";
var fragmentShaderSource = "\
precision highp float; \n\
varying vec3 vertexcolor; \n\
void main(void) \n\
{ \n\
gl_FragColor = vec4(vertexcolor, 1.0); \n\
} \n\
";
// create the vertex shader
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
// create the fragment shader
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
// Create the shader program
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
// If creating the shader program failed, we show compilation and linking errors.
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Unable to initialize the shader program.");
var str = "";
str += "VS:\n" + gl.getShaderInfoLog(vertexShader) + "\n\n";
str += "FS:\n" + gl.getShaderInfoLog(fragmentShader) + "\n\n";
str += "PROG:\n" + gl.getProgramInfoLog(shaderProgram);
alert(str);
}
}
///// Draw the given triangle interpolating vertices color.
function renderTriangle(gl) {
// Clear the framebuffer of the rendering context
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// enable the current shader program
gl.useProgram(shaderProgram);
// connect the buffer containing the vertices of the triangle with the position attribute
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
aPositionIndex = gl.getAttribLocation(shaderProgram, "a_position");
gl.enableVertexAttribArray(aPositionIndex);
gl.vertexAttribPointer(aPositionIndex, 3, gl.FLOAT, false, 0, 0);
// connect the buffer containing the color of each vertex with the color attribute
gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
aVertexColor = gl.getAttribLocation(shaderProgram, "a_color");
gl.enableVertexAttribArray(aVertexColor);
gl.vertexAttribPointer(aVertexColor, 3, gl.FLOAT, false, 0, 0);
// start to draw (!)
gl.drawArrays(gl.TRIANGLES, 0, numVertices);
// disable the current shading program
gl.useProgram(null);
}
I changed your code a bit. See if it works:
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Rendering (colorful)</title>
<body>
<p> Number of Triangles <input id="numTris" type="number"></p>
<p>
R1 <input id="R1" type="number"/>
G1 <input id="G1" type="number"/>
B1 <input id="B1" type="number">
</p><p>
R2 <input id="R2" type="number"/>
G2 <input id="G2" type="number"/>
B2 <input id="B2" type="number"/>
</p><p>
<input type="button" onclick="solid(); start()" value="Solid?">
<input type="button" onclick="interp(); start()" value="Interpolated?">
</p>
<script type="text/javascript" src="js/rendering-filled-circle.js"></script>
<center>
<canvas id="canvas" width="800" height="800">
If you are seeing this message your web browser does not support the HTML5 <canvas> element.
</canvas>
</center>
</body>
<script type="text/javascript">
// WebGL rendering context
var colorType = false;
var size = height;
var gl = null;
function solid(){
colorType = true;
}
function interp(){
colorType = false;
}
function draw() {
renderTriangle(gl);
}
function start() {
var canvas = document.getElementById("canvas");
// Initialize the OpenGL rendering context
gl = canvas.getContext("experimental-webgl");
// Only continue if WebGL is available and working
if (gl) {
// initialize shader programs
initShaders(gl);
// initialize a very simple scene, a triangle
initBuffers(gl, colorType, size);
// call the draw() function every 20 milliseconds
setInterval(draw, 20);
}
else {
alert("WebGL initialization failed! Your browser does not support WebGL or it is not properly configured.");
}
}
</script>
</html>
In my JS, I was trying to set global variables to the values from the input boxes in my HTML.
In my HTML I called functions that were within the JS, but of course these functions had no access to the global variables with the proper values, because these values were not set yet.
I fixed the problem by simply calling getElementByID() within the individual functions in my JS that my HTML was calling, instead of doing so outside the functions, as global variables.
I'm trying to render a sphere in webgl, im using a method from webglfundamentals but somehow in my programm the sphere is just rendered to 20%. In chrome i get this error: L ERROR :GL_INVALID_OPERATION : glDrawArrays: attempt to access out of range vertices in attribute 1
var canvas;
var gl;
var index = 0;
var pointsArray = [];
var normalsArray = [];
var indexArray = [];
var colorArray = [];
var near = -10;
var far = 10;
var radius = 1.5;
var theta = 0.0;
var phi = 0.0;
var dr = 5.0 * Math.PI/180.0;
var left = -3.0;
var right = 3.0;
var ytop =3.0;
var bottom = -3.0;
var va = vec4(0.0, 0.0, -1.0,1);
var vb = vec4(0.0, 0.942809, 0.333333, 1);
var vc = vec4(-0.816497, -0.471405, 0.333333, 1);
var vd = vec4(0.816497, -0.471405, 0.333333,1);
var lightPosition = vec4(0.0, 1.0, 1.0, 0.0 );
var lightAmbient = vec4(0.2, 0.2, 0.2, 1.0 );
var lightDiffuse = vec4( 1.0, 1.0, 1.0, 1.0 );
var lightSpecular = vec4( 1.0, 1.0, 1.0, 1.0 );
var materialAmbient = vec4( 1.0, 0.0, 1.0, 1.0 );
var materialDiffuse = vec4( 1.0, 0.0, 0.0, 1.0 );
var materialSpecular = vec4( 1.0, 0.8, 0.0, 1.0 );
var materialShininess = 100.0;
var ctm;
var ambientColor, diffuseColor, specularColor;
var texture;
var modelViewMatrix, projectionMatrix;
var modelViewMatrixLoc, projectionMatrixLoc;
var eye;
var at = vec3(0.0, 0.0, 0.0);
var up = vec3(0.0, 1.0, 0.0);
var iBuffer;
function createSphereVertices(
radius,
subdivisionsAxis,
subdivisionsHeight,
opt_startLatitudeInRadians,
opt_endLatitudeInRadians,
opt_startLongitudeInRadians,
opt_endLongitudeInRadians) {
if (subdivisionsAxis <= 0 || subdivisionsHeight <= 0) {
throw Error('subdivisionAxis and subdivisionHeight must be > 0');
}
opt_startLatitudeInRadians = opt_startLatitudeInRadians || 0;
opt_endLatitudeInRadians = opt_endLatitudeInRadians || Math.PI;
opt_startLongitudeInRadians = opt_startLongitudeInRadians || 0;
opt_endLongitudeInRadians = opt_endLongitudeInRadians || (Math.PI * 2);
var latRange = opt_endLatitudeInRadians - opt_startLatitudeInRadians;
var longRange = opt_endLongitudeInRadians - opt_startLongitudeInRadians;
// We are going to generate our sphere by iterating through its
// spherical coordinates and generating 2 triangles for each quad on a
// ring of the sphere.
// var numVertices = (subdivisionsAxis + 1) * (subdivisionsHeight + 1);
var positions = [];
// var normals = webglUtils.createAugmentedTypedArray(3, numVertices);
// var texCoords = webglUtils.createAugmentedTypedArray(2 , numVertices);
// Generate the individual vertices in our vertex buffer.
for (var y = 0; y <= subdivisionsHeight; y++) {
for (var x = 0; x <= subdivisionsAxis; x++) {
// Generate a vertex based on its spherical coordinates
var u = x / subdivisionsAxis;
var v = y / subdivisionsHeight;
var theta = longRange * u;
var phi = latRange * v;
var sinTheta = Math.sin(theta);
var cosTheta = Math.cos(theta);
var sinPhi = Math.sin(phi);
var cosPhi = Math.cos(phi);
var ux = cosTheta * sinPhi;
var uy = cosPhi;
var uz = sinTheta * sinPhi;
positions.push(vec4(radius * ux, radius * uy, radius * uz,1.0));
normalsArray.push(vec4(ux, uy, uz,1.0));
// texCoords.push(1 - u, v);
}
}
var numVertsAround = subdivisionsAxis + 1;
// var indices = webglUtils.createAugmentedTypedArray(3, subdivisionsAxis * subdivisionsHeight * 2, Uint16Array);
for (var x = 0; x < subdivisionsAxis; x++) {
for (var y = 0; y < subdivisionsHeight; y++) {
// Make triangle 1 of quad.
pointsArray.push(positions[(y + 0) * numVertsAround + x]);
pointsArray.push(positions[(y + 0) * numVertsAround + x + 1]);
pointsArray.push(positions[(y + 1) * numVertsAround + x]);
// Make triangle 2 of quad.
pointsArray.push(positions[(y + 1) * numVertsAround + x]);
pointsArray.push(positions[(y + 0) * numVertsAround + x + 1]);
pointsArray.push(positions[(y + 1) * numVertsAround + x + 1]);
index +=6;
}
}
}
window.onload = function init() {
canvas = document.getElementById( "gl-canvas" );
gl = WebGLUtils.setupWebGL( canvas );
if ( !gl ) { alert( "WebGL isn't available" ); }
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
gl.enable(gl.DEPTH_TEST);
//
// Load shaders and initialize attribute buffers
//
var program = initShaders( gl, "vertex-shader", "fragment-shader" );
gl.useProgram( program );
createSphereVertices(1,12,12);
ambientProduct = mult(lightAmbient, materialAmbient);
diffuseProduct = mult(lightDiffuse, materialDiffuse);
specularProduct = mult(lightSpecular, materialSpecular);
var nBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, nBuffer);
gl.bufferData( gl.ARRAY_BUFFER, flatten(normalsArray), gl.STATIC_DRAW );
var vNormal = gl.getAttribLocation( program, "vNormal" );
gl.vertexAttribPointer( vNormal, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vNormal);
var vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, flatten(pointsArray), gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation( program, "vPosition");
gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
modelViewMatrixLoc = gl.getUniformLocation( program, "modelViewMatrix" );
projectionMatrixLoc = gl.getUniformLocation( program, "projectionMatrix" );
gl.uniform4fv( gl.getUniformLocation(program,
"ambientProduct"),flatten(ambientProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"diffuseProduct"),flatten(diffuseProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"specularProduct"),flatten(specularProduct) );
gl.uniform4fv( gl.getUniformLocation(program,
"lightPosition"),flatten(lightPosition) );
gl.uniform1f( gl.getUniformLocation(program,
"shininess"),materialShininess );
render();
}
function render() {
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
eye = vec3(radius*Math.sin(theta)*Math.cos(phi),
radius*Math.sin(theta)*Math.sin(phi), radius*Math.cos(theta));
modelViewMatrix = lookAt(eye, at , up);
projectionMatrix = ortho(left, right, bottom, ytop, near, far);
gl.uniformMatrix4fv(modelViewMatrixLoc, false, flatten(modelViewMatrix) );
gl.uniformMatrix4fv(projectionMatrixLoc, false, flatten(projectionMatrix) );
for( var i=0; i<index; i+=3)
gl.drawArrays( gl.TRIANGLES, i, 3);
window.requestAnimFrame(render);
}
project1.html
<!DOCTYPE html>
<html>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec4 vNormal;
varying vec3 N, L, E;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform vec4 lightPosition;
void main()
{
vec3 pos = -(modelViewMatrix * vPosition).xyz;
vec3 light = lightPosition.xyz;
L = normalize( light - pos );
E = -pos;
N = normalize( (modelViewMatrix*vNormal).xyz);
gl_Position = projectionMatrix * modelViewMatrix * vPosition;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 ambientProduct;
uniform vec4 diffuseProduct;
uniform vec4 specularProduct;
uniform float shininess;
varying vec3 N, L, E;
void main()
{
vec4 fColor;
vec3 H = normalize( L + E );
vec4 ambient = ambientProduct;
float Kd = max( dot(L, N), 0.0 );
vec4 diffuse = Kd*diffuseProduct;
float Ks = pow( max(dot(N, H), 0.0), shininess );
vec4 specular = Ks * specularProduct;
if( dot(L, N) < 0.0 ) specular = vec4(0.0, 0.0, 0.0, 1.0);
fColor = ambient + diffuse +specular;
fColor.a = 1.0;
gl_FragColor = fColor;
}
</script>
<script type="text/javascript" src="./Common/webgl-utils.js"></script>
<script type="text/javascript" src="./Common/initShaders.js"></script>
<script type="text/javascript" src="./Common/MV.js"></script>
<script type="text/javascript" src="project1.js"></script>
<body>
<canvas id="gl-canvas" width="512" height="512">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
</html>
This is what i get only a unfinished sphere:
I don't know this is actual correct answer since I didn't check this by running codes.
But, I just guess why this error occured.
Major reason of "out of range in attribute variable"
Basically, this will happen when the buffer you passed didn't have enough length.
Therefore, you need to check the count of buffer source you created.
And your error is ":GL_INVALID_OPERATION : glDrawArrays: attempt to access out of range vertices in attribute 1". In this case, attribute 1 in the error message means "vNormal" attribute in your GLSL code.
Because it is 2nd attribute variable in your GLSL code.
(If the error code says attribute 0, this problem should be made by vPosition)
As long as I saw your code, I think there is mismatch of length with normal buffer and position buffer.
Surface count in your code : Subdivision Height * Subdivision Axis * 2
Position element count in your code:Subdivision Height * Subdivision Axis *6
Normal element count in your code: Subdivision Height * Subdivision Axis * 4
I guess this is the reason of the problem.
Each vertex must have position and normal in this case, so your normal element count must be Subdivision Height * Subdivision Axis * 6
2nd argument in gl.vertexAttribPointer
I think you have mistake about 2nd argument in gl.vertexAttribPointer.
This is the count which means how many float elements needs to be passed each vertex.
In this case, you pushed 3 float elements for each vertex. So, you needs to specify 3 as argument for vPosition even if you used vec4 in your GLSL code.
By this argument, GPU can split these buffers for each vertex and pass them into vertex shader parallely.
4th element in vector
This is not strongly related to your question.But I found the code can be a reason of bug.
positions.push(vec4(radius * ux, radius * uy, radius * uz,1.0));
normalsArray.push(vec4(ux, uy, uz,1.0));
You need to understand what the meaning of 4th element in vec4. When this vector means coordinates, 4th element must be 1.However, when this vector means directions, 4th element must be 0.
Because, if this means directions, direction can not be affected by translation transform. ( I suggest you to learn affine transform to understand this)
So, you need to rewrite that code like this.
positions.push(vec4(radius * ux, radius * uy, radius * uz,1.0));
normalsArray.push(vec4(ux, uy, uz,0));
I hope this answer could help you...