Okay so I'm new to html and javascript right now. My task is to draw circles, I have looked at countless examples and my code doesn't draw anything. Here is what I have so far.
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPos;
void main()
{
gl_PointSize = 10.0;
gl_Position = vPos;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
</script>
<script type="text/javascript" src="webgl-utils.js"></script>
<script type="text/javascript" src="initshaders.js"></script>
<script type="text/javascript" src="lines.js"></script>
<script type="text/javascript">
var canvas;
var gl;
window.onload = init;
function init() {
canvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
if (!gl) {
alert("WebGL isn't available");
}
var program = initShaders(gl, "vertex-shader", "fragment-shader");
var vPos = gl.getAttribLocation(program, "vPos");
var colorUniformLocation = gl.getUniformLocation(program, "u_color");
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0.9, 0.9, 0.9, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.enableVertexAttribArray(vPos);
gl.vertexAttribPointer(vPos, 2, gl.FLOAT, false, 0, 0);
var red = 0.0;
var green = 0.0;
var blue = 1.0;
var vertices = circle(100,100,200);
for (var i=0; i<vertices.length; i++)
{
gl.uniform4f(colorUniformLocation, red, green, blue, 1.0);
gl.drawArrays(gl.POINTS, i, 1);
}
}
</script>
<body>
<canvas id="gl-canvas" width="1000" height="1000">
Oops... your browser doesn't support HTML5's Canvas elements!
</canvas>
</body>
</html>
The error seems to happen at var vertices = circle(100,100,200);
My circle function looks like this.
function getPoints(x,y)
{
var points = [];
points.push(x+x0,y+y0);
points.push(-x+x0,y+y0);
points.push(x+x0,-y+y0);
points.push(-x+x0,-y+y0);
points.push(y+y0,x+x0);
points.push(-y+y0,x+x0);
points.push(y+y0,-x+x0);
points.push(-y+y0,-x+x0);
return points;
}
function circle(x0, y0, radius)
{
var vertices = [];
var points = [];
var pk = 1- radius;
var xk = radius;
var yk = 0;
//var pk = ((xk +1)^2) + ((yk -.5)^2) - (radius^2);
points = getPoints(x0,y0);
for(var i = 0 ; i < points.length; i++)
{
vertices.push(points[i]);
}
while(xk>yk)
{
yk++;
if(pk <0)
{
points = getPoints(xk+1,yk);
pk = pk + (2* yk)+1;
}
else
{
points = getPoints(xk+1,yk-1);
xk--;
pk = pk + (2* yk) -(2* xk)+1;
}
for(var i = 0 ; i < points.length; i++)
{
vertices.push(points[i]);
}
}
return vertices;
}
From what I can tell the algorithm is correct, but for some reason the var vertices isn't returning correctly, because when I launch the file it draws the background but the circle is nowhere to be seen. Please help!
UPDATE
So I have fixed one problem, but my program is still not printing anything out. my circle function now looks like this.
function drawCircle(x0, y0, radius)
{
var vertices = [];
var pk = 1- radius;
var xk = 0;
var yk = radius;
//vertices.push(100,100); I am using these for error checking
//vertices.push(200,100); To see if vertices returns anything at all
//vertices.push(300,100); and from what i can tell it doesnt.
//vertices.push(400,100);
while(xk<yk)
{
xk++;
if(pk <0)
{
pk = pk + (2* xk)+1;
}
else
{
yk--;
pk = pk + (2* xk) -(2* yk)+1;
}
vertices.push(x+x0,y+y0); //This should be adding all symmetric
vertices.push(-x+x0,y+y0); //points from the first one around the
vertices.push(x+x0,-y+y0); //circle
vertices.push(-x+x0,-y+y0);
vertices.push(y+y0,x+x0);
vertices.push(-y+y0,x+x0);
vertices.push(y+y0,-x+x0);
vertices.push(-y+y0,-x+x0);
}
return vertices;
}
Related
I've been working on a WebGl animation program where there are buttons that you can choose to alter a triangle vertice location based on where you click on the screen as well as a slider that runs a recursive function that divides the triangle based on the input. The part i'm stuck on is my last radio button that is supposed to constantly rotate my triangle including the edits to whatever previous changed were made to the triangle with the buttons.
Every time I run my application it gives me a warning pointing to javascript line 149 my render2 function: gl.uniform1f(thetaLoc, theta); saying: sierpinski-extended.js:149 WebGL: INVALID_OPERATION: uniform1f: location not for current program.
The code below isn't going to work because I need to add external libraries and i'm not sure how to. Though I have uploaded my project on my website: https://www.cis.gvsu.edu/~nunezjo/labs/sierpinski-extended/sierpinski-extended.html
Here's an example of my working rotation with no other attributes or buttons added to the mix.
https://www.cis.gvsu.edu/~nunezjo/labs/triangle-rotate/triangle-rotate.html
You can inspect element there if needed. Any help would be awesome! I'm so stuck.
JAVASCRIPT
"use strict";
var canvas;
var gl;
var points = [];
var numTimesToSubdivide = 0;
var theta = 0.0;
var thetaLoc;
var x1 = -1;
var y1 = -1;
var x2 = 0;
var y2 = 1;
var x3 = 1;
var y3 = -1;
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 corners of our gasket with three points.
var vertices = [
vec2(x1, y1),
vec2(x2, y2),
vec2(x3, y3)
];
divideTriangle(vertices[0], vertices[1], vertices[2],
numTimesToSubdivide);
//
// Configure WebGL
//
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.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, 50000, gl.STATIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, flatten(points));
// Associate out shader variables with our data buffer
var vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
document.getElementById("slider").onchange = function(event) {
numTimesToSubdivide = parseInt(event.target.value);
};
canvas.addEventListener("mouseup", function(event) {
var rect = gl.canvas.getBoundingClientRect();
var newx = (event.clientX - rect.left) / canvas.width * 2 - 1;
var newy = (event.clientY - rect.top) / canvas.height * -2 + 1;
console.log(newx, newy);
var vertex_id = document.querySelector('input[name="vertex"]:checked').value;
if (vertex_id == 0) {
x1 = newx;
y1 = newy;
} else if (vertex_id == 1) {
x2 = newx;
y2 = newy;
} else if (vertex_id == 2) {
x3 = newx;
y3 = newy;
} else {
thetaLoc = gl.getUniformLocation(program, "theta");
render2(vertex_id);
}
});
render1();
};
function triangle(a, b, c) {
points.push(a, b, c);
}
function divideTriangle(a, b, c, count) {
// check for end of recursion
if (count === 0) {
triangle(a, b, c);
} else {
//bisect the sides
var ab = mix(a, b, 0.5);
var ac = mix(a, c, 0.5);
var bc = mix(b, c, 0.5);
--count;
// three new triangles
divideTriangle(a, ab, ac, count);
divideTriangle(c, ac, bc, count);
divideTriangle(b, bc, ab, count);
}
}
window.onload = init;
function render1() {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length);
points = [];
requestAnimFrame(init);
}
function render2(button_id) {
gl.clear(gl.COLOR_BUFFER_BIT);
theta += 0.092;// this is the amount that will be rotating the object the bigger the number the faster.
gl.uniform1f(thetaLoc, theta);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, points.length);
while (button_id == 3) { // while rotate button is pressed run loop on render 2 else call init
init(); // running init first so it can keep track if the button_id changes.
requestAnimFrame(render2);
}
}
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title> Sierpinski Extended</title>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition; uniform float theta; void main() { gl_Position = vPosition; float s = sin( theta ); float c = cos( theta ); gl_Position.x = -s * vPosition.y + c * vPosition.x; gl_Position.y = s * vPosition.x + c * vPosition.y; gl_Position.z
= 0.0; gl_Position.w = 1.0; }
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float; void main() { gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); }
</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="sierpinski-extended.js"></script>
</head>
<body>
<div>
recursive steps 0 <input id="slider" type="range" min="0" max="6" step="1" value="0" /> 6
</div>
<div>
Vertex:
<input type="radio" name="vertex" value="0" checked> 0
<input type="radio" name="vertex" value="1"> 1
<input type="radio" name="vertex" value="2"> 2
<input type="radio" name="vertex" value="3"> rotate
</div>
<canvas id="gl-canvas" width="512" height="512">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
</html>
I am new to webgl and I've been trying to pass an attribute to the fragment shader using varying in order to change all the triangles to random colors just like this:
But right now, all the triangles are just in white color, and I'm not sure what did wrong. Also, how do I do to make the triangles fit into the canvas? PLease help, thank you.
// Application info.
var app = app || {};
var nbTriangles = 20;
function getContextGL(canvasElement) {
var can = document.getElementById(canvasElement);
if (can == null) {
return [null, null];
}
var gl = can.getContext("webgl");
return [can, gl];
}
function createShaderFromElement(gl, id) {
// Grab the script element.
var scriptElt = document.getElementById(id);
if (!scriptElt) {
return null;
}
// Retrieve the source.
var scriptSource = scriptElt.textContent;
// Identify shader type based on element type.
var shaderObj;
if (scriptElt.type == "x-shader/x-fragment") {
shaderObj = gl.createShader(gl.FRAGMENT_SHADER);
}
else if (scriptElt.type == "x-shader/x-vertex") {
shaderObj = gl.createShader(gl.VERTEX_SHADER);
}
else {
return null;
}
// Compile and check status.
gl.shaderSource(shaderObj, scriptSource);
gl.compileShader(shaderObj);
var ok = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
if (!ok) {
var msgError = gl.getShaderInfoLog(shaderObj);
alert(msgError);
gl.deleteShader(shader);
return null;
}
return shaderObj;
}
function buildProgram(gl, vertexShader, fragmentShader) {
if (!vertexShader || !fragmentShader) {
return null;
}
var progObject = gl.createProgram();
if (!progObject) {
alert("Can't create program object.");
return;
}
gl.attachShader(progObject, vertexShader);
gl.attachShader(progObject, fragmentShader);
gl.linkProgram(progObject);
var ok = gl.getProgramParameter(progObject, gl.LINK_STATUS);
if (!ok) {
var msgError = gl.getProgramInfoLog(progObject);
alert(msgError);
gl.deleteProgram(progObject);
return null;
}
return progObject;
}
function initGL() {
app.gl.viewport(0, 0, app.can.width, app.can.height);
app.gl.clearColor(0., 0., 0., 1.0);
app.gl.clear(app.gl.COLOR_BUFFER_BIT);
var vs = createShaderFromElement(app.gl, "vs");
var fs = createShaderFromElement(app.gl, "fs");
app.progObject = buildProgram(app.gl, vs, fs);
app.gl.useProgram(app.progObject);
}
function initScene() {
var gl = app.gl;
// Creer le buffer de geometrie (vertex)
//
var positions = [];
for (var i = 0; i < nbTriangles; ++i) {
var orig = [0.0, 1.0, 1.0, -1.0, -1.0, -1.0];
var scale = Math.random() * 0.2;
var Trans = [Math.random() * 1.6, Math.random() * 1.6];
var P0 = [
orig[0] * scale + Trans[0],
orig[1] * scale + Trans[1],
];
var P1 = [
orig[2] * scale + Trans[0],
orig[3] * scale + Trans[1],
];
var P2 = [
orig[4] * scale + Trans[0],
orig[5] * scale + Trans[1],
];
positions.push(...P0, ...P1, ...P2);
}
var colors = [];
for (var i = 0; i < nbTriangles; ++i) {
var newColor = [Math.random() * 256, Math.random() * 256, Math.random() * 256, 1.0];
colors.push(...newColor);
}
// Creer un nouveau buffer vide.
var posBuffer = gl.createBuffer();
var colorBuffer = gl.createBuffer();
// Ref sur l'attribut "pos" dans le vertex shader.
var posLocation = gl.getAttribLocation(app.progObject, "pos");
var colorLocation = gl.getAttribLocation(app.progObject, "color");
// Activer le buffer. Toute operation sur buffer
// sera appliquer a posBuffer (il est actif!).
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
gl.enableVertexAttribArray(posLocation);
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false /*no normalization*/, 0 /*stride*/, 0 /*offset*/);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(colorLocation);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
}
function render() {
var gl = app.gl;
gl.clear(gl.COLOR_BUFFER_BIT)
// Dessiner le buffer.
gl.drawArrays(gl.TRIANGLES, 0, 3 * nbTriangles);
}
function init() {
[app.can, app.gl] = getContextGL('can');
if (app.can == null || app.gl == null) {
alert("Can't init canvas or context");
return;
}
app.can.width = app.can.height * (app.can.clientWidth / app.can.clientHeight);
var rect = app.can.getBoundingClientRect();
app.scaleX = app.can.width / rect.width;
app.scaleY = app.can.height / rect.height;
initGL();
initScene();
render();
}
div
{
}
#main-div
{
display:inline-block;
}
#viewport, #manager
{
float: left;
margin: auto;
}
.color
{
width:100px;
height:50px;
}
.blue{
background:#0f0;
}
#viewport
{
width: 600px;
height:700px;
}
#can
{
width: 600px;
height: 500px;
border:1px solid orange;
}
#manager
{
width: 200px;
height:300px;
padding: 0 0 0 5px;
}
#obj-list
{
width: 200px;
}
<!DOCTYPE html>
<html>
<head>
<title>Colored Quad</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="colored_quad.js"></script>
<script id="vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 pos;
attribute vec4 color;
varying vec4 fcolor;
void main()
{
vec4 pt = vec4(pos, 0.0, 1.0);
gl_Position = pt;
fcolor=color;
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fcolor;
void main()
{
vec4 color= fcolor;
gl_FragColor = color;
}
</script>
</head>
<body onload="init();">
<div id="main-div">
<div id="viewport">
<canvas id="can" >Your browser doesn't seem to support canvas!</canvas>
</div>
</div>
</body>
</html>
The the color attribute has a floating point type:
attribute vec4 color;
gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
Hence the color values have to be specified in range [0.0, 1.0] rather than [0, 256]:
var newColor = [Math.random() * 256, Math.random() * 256, Math.random() * 256, 1.0];
var newColor = [Math.random(), Math.random(), Math.random(), 1.0];
Furthermore, each triangle has 3 vertices (corners). Each vertex coordinate has to be associated to a separate color attribute. So each triangle has to have 3 color attributes, too:
var colors = [];
for (var i = 0; i < nbTriangles; ++i) {
var newColor = [Math.random(), Math.random(), Math.random(), 1.0];
colors.push(...newColor);
colors.push(...newColor);
colors.push(...newColor);
}
// Application info.
var app = app || {};
var nbTriangles = 20;
function getContextGL(canvasElement) {
var can = document.getElementById(canvasElement);
if (can == null) {
return [null, null];
}
var gl = can.getContext("webgl");
return [can, gl];
}
function createShaderFromElement(gl, id) {
// Grab the script element.
var scriptElt = document.getElementById(id);
if (!scriptElt) {
return null;
}
// Retrieve the source.
var scriptSource = scriptElt.textContent;
// Identify shader type based on element type.
var shaderObj;
if (scriptElt.type == "x-shader/x-fragment") {
shaderObj = gl.createShader(gl.FRAGMENT_SHADER);
}
else if (scriptElt.type == "x-shader/x-vertex") {
shaderObj = gl.createShader(gl.VERTEX_SHADER);
}
else {
return null;
}
// Compile and check status.
gl.shaderSource(shaderObj, scriptSource);
gl.compileShader(shaderObj);
var ok = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
if (!ok) {
var msgError = gl.getShaderInfoLog(shaderObj);
alert(msgError);
gl.deleteShader(shader);
return null;
}
return shaderObj;
}
function buildProgram(gl, vertexShader, fragmentShader) {
if (!vertexShader || !fragmentShader) {
return null;
}
var progObject = gl.createProgram();
if (!progObject) {
alert("Can't create program object.");
return;
}
gl.attachShader(progObject, vertexShader);
gl.attachShader(progObject, fragmentShader);
gl.linkProgram(progObject);
var ok = gl.getProgramParameter(progObject, gl.LINK_STATUS);
if (!ok) {
var msgError = gl.getProgramInfoLog(progObject);
alert(msgError);
gl.deleteProgram(progObject);
return null;
}
return progObject;
}
function initGL() {
app.gl.viewport(0, 0, app.can.width, app.can.height);
app.gl.clearColor(0., 0., 0., 1.0);
app.gl.clear(app.gl.COLOR_BUFFER_BIT);
var vs = createShaderFromElement(app.gl, "vs");
var fs = createShaderFromElement(app.gl, "fs");
app.progObject = buildProgram(app.gl, vs, fs);
app.gl.useProgram(app.progObject);
}
function initScene() {
var gl = app.gl;
// Creer le buffer de geometrie (vertex)
//
var positions = [];
for (var i = 0; i < nbTriangles; ++i) {
var orig = [0.0, 1.0, 1.0, -1.0, -1.0, -1.0];
var scale = Math.random() * 0.2;
var Trans = [Math.random() * 1.6, Math.random() * 1.6];
var P0 = [
orig[0] * scale + Trans[0],
orig[1] * scale + Trans[1],
];
var P1 = [
orig[2] * scale + Trans[0],
orig[3] * scale + Trans[1],
];
var P2 = [
orig[4] * scale + Trans[0],
orig[5] * scale + Trans[1],
];
positions.push(...P0, ...P1, ...P2);
}
var colors = [];
for (var i = 0; i < nbTriangles; ++i) {
var newColor = [Math.random(), Math.random(), Math.random(), 1.0];
colors.push(...newColor);
colors.push(...newColor);
colors.push(...newColor);
}
// Creer un nouveau buffer vide.
var posBuffer = gl.createBuffer();
var colorBuffer = gl.createBuffer();
// Ref sur l'attribut "pos" dans le vertex shader.
var posLocation = gl.getAttribLocation(app.progObject, "pos");
var colorLocation = gl.getAttribLocation(app.progObject, "color");
// Activer le buffer. Toute operation sur buffer
// sera appliquer a posBuffer (il est actif!).
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
gl.enableVertexAttribArray(posLocation);
gl.vertexAttribPointer(posLocation, 2, gl.FLOAT, false /*no normalization*/, 0 /*stride*/, 0 /*offset*/);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(colorLocation);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
}
function render() {
var gl = app.gl;
gl.clear(gl.COLOR_BUFFER_BIT)
// Dessiner le buffer.
gl.drawArrays(gl.TRIANGLES, 0, 3 * nbTriangles);
}
function init() {
[app.can, app.gl] = getContextGL('can');
if (app.can == null || app.gl == null) {
alert("Can't init canvas or context");
return;
}
app.can.width = app.can.height * (app.can.clientWidth / app.can.clientHeight);
var rect = app.can.getBoundingClientRect();
app.scaleX = app.can.width / rect.width;
app.scaleY = app.can.height / rect.height;
initGL();
initScene();
render();
}
div
{
}
#main-div
{
display:inline-block;
}
#viewport, #manager
{
float: left;
margin: auto;
}
.color
{
width:100px;
height:50px;
}
.blue{
background:#0f0;
}
#viewport
{
width: 600px;
height:700px;
}
#can
{
width: 600px;
height: 500px;
border:1px solid orange;
}
#manager
{
width: 200px;
height:300px;
padding: 0 0 0 5px;
}
#obj-list
{
width: 200px;
}
<!DOCTYPE html>
<html>
<head>
<title>Colored Quad</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="colored_quad.js"></script>
<script id="vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 pos;
attribute vec4 color;
varying vec4 fcolor;
void main()
{
vec4 pt = vec4(pos, 0.0, 1.0);
gl_Position = pt;
fcolor=color;
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fcolor;
void main()
{
vec4 color= fcolor;
gl_FragColor = color;
}
</script>
</head>
<body onload="init();">
<div id="main-div">
<div id="viewport">
<canvas id="can" >Your browser doesn't seem to support canvas!</canvas>
</div>
</div>
</body>
</html>
I'd like to be able to wrap an image of the Earth around my sphere. Using any stock image off of Google.
I've been looking around and I found only answers with three.js, and I'm looking for the most simple way to achieve my goal without that. Below is all of the documentation of a sphere I'd like to texture. If you copy and paste it you should be able to see a regular sphere with some shading on it.
From my WebGL book,
.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Point lighted sphere</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
<script src="../lib/webgl-utils.js"></script>
<script src="../lib/webgl-debug.js"></script>
<script src="../lib/cuon-utils.js"></script>
<script src="../lib/cuon-matrix.js"></script>
<script src="PointLightedSphere.js"></script>
</body>
</html>
.js
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
// 'attribute vec4 a_Color;\n' + // Defined constant in main()
'attribute vec4 a_Normal;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'uniform mat4 u_ModelMatrix;\n' + // Model matrix
'uniform mat4 u_NormalMatrix;\n' + // Transformation matrix of the normal
'uniform vec3 u_LightColor;\n' + // Light color
'uniform vec3 u_LightPosition;\n' + // Position of the light source
'uniform vec3 u_AmbientLight;\n' + // Ambient light color
'varying vec4 v_Color;\n' +
'void main() {\n' +
' vec4 color = vec4(1.0, 1.0, 1.0, 1.0);\n' + // Sphere color
' gl_Position = u_MvpMatrix * a_Position;\n' +
// Calculate a normal to be fit with a model matrix, and make it 1.0 in length
' vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
// Calculate world coordinate of vertex
' vec4 vertexPosition = u_ModelMatrix * a_Position;\n' +
// Calculate the light direction and make it 1.0 in length
' vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n' +
// The dot product of the light direction and the normal
' float nDotL = max(dot(lightDirection, normal), 0.0);\n' +
// Calculate the color due to diffuse reflection
' vec3 diffuse = u_LightColor * color.rgb * nDotL;\n' +
// Calculate the color due to ambient reflection
' vec3 ambient = u_AmbientLight * color.rgb;\n' +
// Add the surface colors due to diffuse reflection and ambient reflection
' v_Color = vec4(diffuse + ambient, color.a);\n' +
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_FragColor = v_Color;\n' +
'}\n';
function main() {
// Retrieve <canvas> element
var canvas = document.getElementById('webgl');
// Get the rendering context for WebGL
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// Initialize shaders
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// Set the vertex coordinates, the color and the normal
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the vertex information');
return;
}
// Set the clear color and enable the depth test
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);
// Get the storage locations of uniform variables and so on
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
if (!u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) {
console.log('Failed to get the storage location');
return;
}
// Set the light color (white)
gl.uniform3f(u_LightColor, 0.8, 0.8, 0.8);
// Set the light direction (in the world coordinate)
gl.uniform3f(u_LightPosition, 5.0, 8.0, 7.0);
// Set the ambient light
gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);
var modelMatrix = new Matrix4(); // Model matrix
var mvpMatrix = new Matrix4();   // Model view projection matrix
var normalMatrix = new Matrix4(); // Transformation matrix for normals
// Pass the model matrix to u_ModelMatrix
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
// Calculate the view projection matrix
mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
mvpMatrix.lookAt(0, 0, 6, 0, 0, 0, 0, 1, 0);
mvpMatrix.multiply(modelMatrix);
// Pass the model view projection matrix to u_MvpMatrix
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
// Calculate the matrix to transform the normal based on the model matrix
normalMatrix.setInverseOf(modelMatrix);
normalMatrix.transpose();
// Pass the transformation matrix for normals to u_NormalMatrix
gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);
// Clear color and depth buffer
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Draw the cube(Note that the 3rd argument is the gl.UNSIGNED_SHORT)
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
}
function initVertexBuffers(gl) { // Create a sphere
var SPHERE_DIV = 13;
var i, ai, si, ci;
var j, aj, sj, cj;
var p1, p2;
var positions = [];
var indices = [];
// Generate coordinates
for (j = 0; j <= SPHERE_DIV; j++) {
aj = j * Math.PI / SPHERE_DIV;
sj = Math.sin(aj);
cj = Math.cos(aj);
for (i = 0; i <= SPHERE_DIV; i++) {
ai = i * 2 * Math.PI / SPHERE_DIV;
si = Math.sin(ai);
ci = Math.cos(ai);
positions.push(si * sj); // X
positions.push(cj); // Y
positions.push(ci * sj); // Z
}
}
// Generate indices
for (j = 0; j < SPHERE_DIV; j++) {
for (i = 0; i < SPHERE_DIV; i++) {
p1 = j * (SPHERE_DIV+1) + i;
p2 = p1 + (SPHERE_DIV+1);
indices.push(p1);
indices.push(p2);
indices.push(p1 + 1);
indices.push(p1 + 1);
indices.push(p2);
indices.push(p2 + 1);
}
}
// Write the vertex property to buffers (coordinates and normals)
// Same data can be used for vertex and normal
// In order to make it intelligible, another buffer is prepared separately
if (!initArrayBuffer(gl, 'a_Position', new Float32Array(positions), gl.FLOAT, 3)) return -1;
if (!initArrayBuffer(gl, 'a_Normal', new Float32Array(positions), gl.FLOAT, 3)) return -1;
// Unbind the buffer object
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// Write the indices to the buffer object
var indexBuffer = gl.createBuffer();
if (!indexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
return indices.length;
}
function initArrayBuffer(gl, attribute, data, type, num) {
// Create a buffer object
var buffer = gl.createBuffer();
if (!buffer) {
console.log('Failed to create the buffer object');
return false;
}
// Write date into the buffer object
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
// Assign the buffer object to the attribute variable
var a_attribute = gl.getAttribLocation(gl.program, attribute);
if (a_attribute < 0) {
console.log('Failed to get the storage location of ' + attribute);
return false;
}
gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
// Enable the assignment of the buffer object to the attribute variable
gl.enableVertexAttribArray(a_attribute);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
return true;
}
I am creating a simple webgl program that puts 3 random vertices on the canvas and connects them into a triangle. I tried to add translation to move the triangle to the right (increase the X value of each vertex), but of course if it goes forever, the triangle will go out of the canvas. Does anyone know how to detect if the vertex has an x value above 1 and if yes, reset the position of the given vertex because my solution doesnt seem to do anything, its like it doesnt even trigger
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
gl.clearColor(0.1, 0.2, 0.2, 1.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
var indices = [0, 0, 0, 0, 0, 0];
for (var points = 0; points < 6; points++) {
indices[points] = (Math.random() * 2) - 1;
//indices[points + 1] = Math.random() < 0.5 ? -1 : 1;
}
var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(indices),
gl.STATIC_DRAW);
var vert = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vert, `
precision mediump float;
attribute vec2 position;
uniform vec2 translation;
void main(){
gl_Position = vec4(position + translation, 0.0, 1.0);
}
`);
gl.compileShader(vert);
var success1 = gl.getShaderParameter(vert, gl.COMPILE_STATUS);
if (!success1) {
// Something went wrong during compilation; get the error
throw gl.getShaderInfoLog(vert);
}
var frag = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(frag, `
precision mediump float;
void main(){
gl_FragColor = vec4(0.3, 0.6, 0.4, 1.0);
}
`);
gl.compileShader(frag);
var success2 = gl.getShaderParameter(frag, gl.COMPILE_STATUS);
if (!success2) {
// Something went wrong during compilation; get the error
throw gl.getShaderInfoLog(frag);
}
var program = gl.createProgram();
gl.attachShader(program, vert);
gl.attachShader(program, frag);
gl.linkProgram(program);
var vertLoc = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(vertLoc, 2, gl.FLOAT, gl.FALSE, 0, 0);
gl.enableVertexAttribArray(vertLoc);
gl.useProgram(program);
var trans = gl.getUniformLocation(program, "translation");
var translation = [0.0, 0.0];
gl.uniform2fv(trans, translation);
gl.drawArrays(gl.TRIANGLES, 0, 3);
function loop() {
gl.clearColor(0.1, 0.2, 0.2, 1.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
translation[0] += 0.01;
gl.uniform2fv(trans, translation);
gl.drawArrays(gl.TRIANGLES, 0, 3);
for (var points = 0; points < 6; points++) {
if (indices[points] % 2 == 0) {
if (indices[points] + translation[0] > 1) {
indices[points] = (Math.random() * 2) - 1;
}
}
//indices[points + 1] = Math.random() < 0.5 ? -1 : 1;
}
requestAnimationFrame(loop);
}
loop();
<canvas id="canvas"></canvas>
To achieve this, consider making the following changes to your code:
remove placement of vertices by translation in your vertex shader to give you "per-vertex" control over placement of the geometry (the translation effectivly means "object-level" placement which isn't what your want here)
when you iterate over the points in your loop(), you checking modulo over the vertex coordinate. You should be performing that check on the iteration index like this: if (points % 2 == 0)
now that the translation concept is gone, update the position of the vertex coordinate, rather the translation array after the modulo check: indices[points] += 0.01;
finally, seeing you're updating the indices vertex data, you'll need to update the webgl buf to ensure your changes are reflected when the next frame is rendered:
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(indices), gl.STATIC_DRAW);
Here's the updated script in full:
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
gl.clearColor(0.1, 0.2, 0.2, 1.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
var indices = [0, 0, 0, 0, 0, 0];
for (var points = 0; points < 6; points++) {
indices[points] = (Math.random() * 2) - 1;
}
var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(indices),
gl.STATIC_DRAW);
var vert = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vert, `
precision mediump float;
attribute vec2 position;
void main(){
gl_Position = vec4(position, 0.0, 1.0);
}
`);
gl.compileShader(vert);
var success1 = gl.getShaderParameter(vert, gl.COMPILE_STATUS);
if (!success1) {
throw gl.getShaderInfoLog(vert);
}
var frag = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(frag, `
precision mediump float;
void main(){
gl_FragColor = vec4(0.3, 0.6, 0.4, 1.0);
}
`);
gl.compileShader(frag);
var success2 = gl.getShaderParameter(frag, gl.COMPILE_STATUS);
if (!success2) {
throw gl.getShaderInfoLog(frag);
}
var program = gl.createProgram();
gl.attachShader(program, vert);
gl.attachShader(program, frag);
gl.linkProgram(program);
var vertLoc = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(vertLoc, 2, gl.FLOAT, gl.FALSE, 0, 0);
gl.enableVertexAttribArray(vertLoc);
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);
function loop() {
gl.clearColor(0.1, 0.2, 0.2, 1.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// Update the vertex data, causing the vertex x coordinate to increase per-frame
for (var points = 0; points < 6; points++) {
// Only process x coordinate
if (points % 2 == 0) {
// Increase x coordinate per-frame
indices[points] += 0.01;
// If x position > 1 reset it to a new random value
if (indices[points] > 1) {
indices[points] = (Math.random() * 2) - 1;
}
}
}
// Update webgl vertex buffer so that updated indices data is rendered
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(indices), gl.STATIC_DRAW);
requestAnimationFrame(loop);
}
loop();
<canvas id="canvas"><canvas>
I've recently started learning Javascript/ WebGL and I've learned enough to put together a simple Mandelbrot fractal renderer. The program works fine but for some reason it won't let me zoom in more than about 20 times, and it starts looking pixellated if I zoom in more. I've had this problem before in other fractal drawing programs I've made, but it usually doesn't become noticeable until about 2^45 zoom. I was thinking maybe it has to do with the max float size in GLSL, but I'm really not sure what the problem is or even how to go about finding the problem. I was just wondering if anyone knows what the cause of this zoom limit is and if there's any way I can increase it? Here's my HTML/ GLSL code:
<html>
<head>
<title>Mandelbrot Set</title>
<style>
body {
margin = 0;
padding = 0;
}
</style>
</head>
<body>
<h3>Click on the fractal to zoom in.</h3>
<canvas id = "canvas" width = "500" height = "500" onclick = "drawFractal();">
Sorry, your browser does not support HTML5.
</canvas>
<script id = "vertexshader" type = "vertexshader">
attribute vec2 a_position;
void main(){
gl_Position = vec4(a_position, 0, 0);
}
</script>
<script id = "fragmentshader" type = "fragmentshader">
precision mediump float;
uniform vec2 u_resolution;
uniform vec2 u_zoomCenter;
uniform float u_zoom;
uniform int u_maxIterations;
uniform float u_colorDiversity;
vec2 f(vec2 z, vec2 c)
{
return vec2(z.x*z.x - z.y*z.y, z.x*z.y*2.0) + c;
}
// Credit to hughsk on GitHub for this hsv to rgb converter
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main(){
vec2 zeroToOne = gl_FragCoord.xy / u_resolution;
vec2 c = u_zoomCenter + (zeroToOne * 4.0 - vec2(2.0)) / u_zoom;
vec2 z = vec2(0.0);
bool escaped = false;
float iterations = 0.0;
for (int i = 0; i < 100000; i++)
{
if (i > u_maxIterations) break;
z = f(z, c);
if (length(z) > 2.0)
{
escaped = true;
iterations = float(i);
break;
}
}
gl_FragColor = escaped ? vec4(hsv2rgb(vec3(iterations * u_colorDiversity, 1.0, 1.0)), 1.0) : vec4(vec3(0.0), 1.0);
}
</script>
<script src = "webgl.js"></script>
</body>
</html>
Here's my "webgl.js" file:
// Compile and link shaders and create program
function createShader(gl, type, source){
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
alert("Error: failed to create shader. Check the console for more information.");
}
function createProgram(gl, vertexShader, fragmentShader){
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (gl.getProgramParameter(program, gl.LINK_STATUS)) return program;
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
alert("Error: failed to create program. Check the console for more information.");
}
// WebGL setup
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
if (!gl){
var gl = canvas.getContext("experimental-webgl");
console.log("WebGL not supported, falling back on experimental WebGL.");
}
if (!gl){
console.log("Experimental WebGL not supported.");
alert("Your browser does not support WebGL. Check the console for more information.");
}
// Create shaders and program
var vertexShaderSource = document.getElementById("vertexshader").text;
var fragmentShaderSource = document.getElementById("fragmentshader").text;
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
var program = createProgram(gl, vertexShader, fragmentShader);
gl.useProgram(program);
// Set up position buffer
var screen = new Float32Array([
-1, -1,
1, -1,
1, 1,
1, 1,
-1, 1,
-1, -1]);
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, screen, gl.STATIC_DRAW);
// Set up position attribute in vertex shader
var a_positionLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(a_positionLocation);
gl.vertexAttribPointer(a_positionLocation, 2, gl.FLOAT, false, 0, 0);
// Set up WebGL window
gl.viewport(0, 0, 500, 500);
gl.clearColor(0, 0, 0, 0);
// Set up uniforms in fragment shader
var u_resolutionLocation = gl.getUniformLocation(program, "u_resolution");
var u_zoomCenterLocation = gl.getUniformLocation(program, "u_zoomCenter");
var u_zoomLocation = gl.getUniformLocation(program, "u_zoom");
var u_maxIterationsLocation = gl.getUniformLocation(program, "u_maxIterations");
var u_colorDiversityLocation = gl.getUniformLocation(program, "u_colorDiversity");
gl.uniform2f(u_resolutionLocation, 500, 500);
// Set up some global variables
var offset_x = 0;
var offset_y = 0;
var zoom = 1;
var iterations = 10000;
var colorDiversity = 0.01;
// Update uniforms based on global variables
function updateUniforms()
{
gl.uniform2f(u_zoomCenterLocation, offset_x, offset_y);
gl.uniform1f(u_zoomLocation, zoom);
gl.uniform1i(u_maxIterationsLocation, iterations);
gl.uniform1f(u_colorDiversityLocation, colorDiversity);
}
// Get mouse position
function getMousePos() {
var rect = canvas.getBoundingClientRect();
return [(event.clientX - rect.left - 250) / 125, (event.clientY - rect.top - 250) / 125];
}
// Draw the fractal
function drawFractal() {
mousePos = getMousePos();
offset_x += mousePos[0] / zoom;
offset_y -= mousePos[1] / zoom;
zoom *= 2;
updateUniforms();
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
// Draw fractal when the page loads
updateUniforms();
gl.drawArrays(gl.TRIANGLES, 0, 6);
Maximal possible zoom depends on the precision of the floating point number you use and your algorithm.
You can increase precision using arbitrary precision numbers, for example mpfr, mpc or arb library