Add Texture to sphere - javascript

I need help figuring out how to add textures to my basketball.
What should i add to my vertex and fragment shaders.
Fragment shader
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
struct MaterialProperties {
vec4 diffuseColor;
vec3 specularColor;
vec3 emissiveColor;
float specularExponent;
};
struct LightProperties {
bool enabled;
vec4 position;
vec3 color;
};
uniform MaterialProperties material; // do two-sided lighting, but assume front and back materials are the same
uniform LightProperties lights[4];
uniform mat3 normalMatrix;
varying vec3 v_normal;
varying vec3 v_eyeCoords;
vec3 lightingEquation( LightProperties light, MaterialProperties material,
vec3 eyeCoords, vec3 N, vec3 V ) {
// N is normal vector, V is direction to viewer.
vec3 L, R; // Light direction and reflected light direction.
if ( light.position.w == 0.0 ) {
L = normalize( light.position.xyz );
}
else {
L = normalize( light.position.xyz/light.position.w - v_eyeCoords );
}
if (dot(L,N) <= 0.0) {
return vec3(0.0);
}
vec3 reflection = dot(L,N) * light.color * material.diffuseColor.rgb;
R = -reflect(L,N);
if (dot(R,V) > 0.0) {
float factor = pow(dot(R,V),material.specularExponent);
reflection += factor * material.specularColor * light.color;
}
return reflection;
}
void main() {
vec3 normal = normalize( normalMatrix*v_normal );
vec3 viewDirection = normalize( -v_eyeCoords); // (Assumes a perspective projection.)
vec3 color = material.emissiveColor;
for (int i = 0; i < 4; i++) {
if (lights[i].enabled) {
if (gl_FrontFacing) {
color += lightingEquation( lights[i], material, v_eyeCoords,
normal, viewDirection);
}
else {
color += lightingEquation( lights[i], material, v_eyeCoords,
-normal, viewDirection);
}
}
}
gl_FragColor = vec4(color,material.diffuseColor.a);
}
The function "world()" is being draw in my "Draw ()" function. The world consist of a scene, but for the sake of readability i will just like the ball i want to add texture to.
function world() {
// Ground
pushMatrix();
mat4.translate(modelview,modelview,[-1,-3,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[9,9,0.5]);
currentColor = [0.0,0.7,0.0,1];
cube.render();
popMatrix();
//Basketball
pushMatrix();
mat4.translate(modelview,modelview,[xval,yval,3]);//
mat4.scale(modelview,modelview,[0.15,0.15,0.15]);
currentColor = [1,0.3,0,1];
sphere.render();
popMatrix();
}
I have a createModel function that holds all my buffers.
I have a createProgram function that creates a program using the vertex and fragment shaders.
I have a initGL function that initializes the webgl context and gets attributes and uniform locations.
I have a init function that is called when the page is loaded.
Complete version of my code:
<!DOCTYPE html>
<meta charset="UTF-8">
<html>
<!--
A demo of lighting and hierarchical modeling using WebGL.
Emissive color is used to make the lights seem to glow yellow when
they are turned on.
Derived from code available from Diskworld-2.html
The Shaders are still complex but have been simplified slightly.
-->
<head>
<title>Project 4 - Shooting basketball</title>
<style>
body {
background-color: #EEEEEE;
}
label {
white-space: pre;
margin-left: 25px;
}
</style>
<!-- This shader is exactly as provided -->
<script type="x-shader/x-vertex" id="vshader-source">
attribute vec3 a_coords;
attribute vec3 a_normal;
uniform mat4 modelview;
uniform mat4 projection;
varying vec3 v_normal;
varying vec3 v_eyeCoords;
void main() {
vec4 coords = vec4(a_coords,1.0);
vec4 eyeCoords = modelview * coords;
gl_Position = projection * eyeCoords;
v_normal = normalize(a_normal);
v_eyeCoords = eyeCoords.xyz/eyeCoords.w;
}
</script>
<!-- This shader has simplified lighting model and equation
Notice the light only has 3 properties now -->
<script type="x-shader/x-fragment" id="fshader-source">
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
struct MaterialProperties {
vec4 diffuseColor;
vec3 specularColor;
vec3 emissiveColor;
float specularExponent;
};
struct LightProperties {
bool enabled;
vec4 position;
vec3 color;
};
uniform MaterialProperties material; // do two-sided lighting, but assume front and back materials are the same
uniform LightProperties lights[4];
uniform mat3 normalMatrix;
varying vec3 v_normal;
varying vec3 v_eyeCoords;
vec3 lightingEquation( LightProperties light, MaterialProperties material,
vec3 eyeCoords, vec3 N, vec3 V ) {
// N is normal vector, V is direction to viewer.
vec3 L, R; // Light direction and reflected light direction.
if ( light.position.w == 0.0 ) {
L = normalize( light.position.xyz );
}
else {
L = normalize( light.position.xyz/light.position.w - v_eyeCoords );
}
if (dot(L,N) <= 0.0) {
return vec3(0.0);
}
vec3 reflection = dot(L,N) * light.color * material.diffuseColor.rgb;
R = -reflect(L,N);
if (dot(R,V) > 0.0) {
float factor = pow(dot(R,V),material.specularExponent);
reflection += factor * material.specularColor * light.color;
}
return reflection;
}
void main() {
vec3 normal = normalize( normalMatrix*v_normal );
vec3 viewDirection = normalize( -v_eyeCoords); // (Assumes a perspective projection.)
vec3 color = material.emissiveColor;
for (int i = 0; i < 4; i++) {
if (lights[i].enabled) {
if (gl_FrontFacing) {
color += lightingEquation( lights[i], material, v_eyeCoords,
normal, viewDirection);
}
else {
color += lightingEquation( lights[i], material, v_eyeCoords,
-normal, viewDirection);
}
}
}
gl_FragColor = vec4(color,material.diffuseColor.a);
}
</script>
<script src="gl-matrix-min.js"></script>
<script src="trackball-rotator.js"></script>
<script src="basic-object-models-IFS.js"></script>
<script>
"use strict";
var gl; // The webgl context.
var xval= 1.3;
var yval= -1.8;
var kval = 0; // Kick leg start
var a_coords_loc; // Location of the a_coords attribute variable in the shader program.
var a_normal_loc; // Location of a_normal attribute
var a_texCoords_loc;
// var texture;
var u_modelview; // Locations for uniform matrices
var u_projection;
var u_normalMatrix;
var u_material; // An object tolds uniform locations for the material.
var u_lights; // An array of objects that holds uniform locations for light properties.
var projection = mat4.create(); // projection matrix
var modelview; // modelview matrix; value comes from rotator
var normalMatrix = mat3.create(); // matrix, derived from modelview matrix, for transforming normal vectors
var rotator; // A TrackballRotator to implement rotation by mouse.
var frameNumber = 0; // frame number during animation (actually only goes up by 0.5 per frame)
var torus, sphere, cone, cylinder, disk, ring, cube; // basic objects, created using function createModel
var matrixStack = []; // A stack of matrices for implementing hierarchical graphics.
var currentColor = [1,1,1,1]; // The current diffuseColor; render() functions in the basic objects set
// the diffuse color to currentColor when it is called before drawing the object.
// Other color properties, which don't change often are handled elsewhere.
var textureURLs = [
"textures/basketball.jpg" // array to hold textures
];
/**
* Draws the image, which consists of either the "world" or a closeup of the "car".
*/
function draw() {
gl.clearColor(0,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(projection, Math.PI/4, 1, 1, 50);
gl.uniformMatrix4fv(u_projection, false, projection );
modelview = rotator.getViewMatrix();
lights();
world();
}
function setLightPosition( u_position_loc, modelview, lightPosition ) {
var transformedPosition = new Float32Array(4);
vec4.transformMat4(transformedPosition, lightPosition, modelview);
gl.uniform4fv(u_position_loc, transformedPosition);
}
function lights() {
gl.uniform1i( u_lights[0].enabled, 1 );
gl.uniform4f( u_lights[0].position, 0,0,1,0 );
gl.uniform3f( u_lights[0].color, 0.15,0.15,0.15 );
// Sun light
gl.uniform1i( u_lights[1].enabled, 1 );
gl.uniform3f( u_lights[1].color, 1,0.3,0.0 );
//Lamp at night
gl.uniform1i( u_lights[2].enabled, 1 );
gl.uniform3f( u_lights[2].color, 0.35, 0.35, 0.1 ); // light 1 is the lamp at night
gl.uniform1f( u_lights[2].attenuation, 2 );
gl.uniform1i( u_lights[3].enabled, 1 );
gl.uniform3f( u_lights[3].color, 0.35, 0.35, 0.1 ); // light 1 is the lamp at night
gl.uniform1f( u_lights[3].attenuation, 2 );
currentColor = [ 0.3, 0.3, 0.3, 1 ];
//SunSetlight
pushMatrix();
mat4.translate(modelview,modelview,[7,-3,-10]);//
mat4.scale(modelview,modelview,[0.8,0.8,0.8]);
setLightPosition(u_lights[1].position, modelview, [1,0,0,0]);
gl.uniform3f( u_material.emissiveColor, 1.5,0.4, 0 );
sphere.render();
gl.uniform3f( u_material.emissiveColor, 0, 0, 0 );
popMatrix();
// lamp for night
pushMatrix();
mat4.translate(modelview,modelview,[-1,-0.8,3]);
mat4.scale(modelview,modelview,[0.4,0.1,0.1]);
setLightPosition(u_lights[2].position, modelview, [0,0,0,1]);
gl.uniform3f( u_material.emissiveColor, 1, 1, 0 );
cube.render();
gl.uniform3f( u_material.emissiveColor, 0, 0, 0 );
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-1,-0.8,-3]);
mat4.scale(modelview,modelview,[0.4,0.1,0.1]);
setLightPosition(u_lights[3].position, modelview, [0,0,0,1]);
gl.uniform3f( u_material.emissiveColor, 1, 1, 0 );
cube.render();
gl.uniform3f( u_material.emissiveColor, 0, 0, 0 );
popMatrix();
}
/**
* Draws a boxman figure using transformation from cubes and cylinders.
* Note for every push their should be a pop. This resets the transformations
*/
function world() {
// Ground
pushMatrix();
mat4.translate(modelview,modelview,[-1,-3,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[9,9,0.5]);
currentColor = [0.0,0.7,0.0,1];
cube.render();
popMatrix();
// Street lamp
pushMatrix();
mat4.translate(modelview,modelview,[-0.8,-4.7,3]);
mat4.scale(modelview,modelview,[0.5,0.5,0.5]);
streetLight();
popMatrix();
// Street lamp
pushMatrix();
mat4.translate(modelview,modelview,[-0.8,-4.7,-3]);
mat4.scale(modelview,modelview,[0.5,0.5,0.5]);
streetLight();
popMatrix();
// Basketball court
pushMatrix();
mat4.translate(modelview,modelview,[2,-2.75,3]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[3,3,0.1]);
currentColor = [0.2,0.2,0.2,1];
cube.render();
popMatrix();
//Road
pushMatrix();
mat4.translate(modelview,modelview,[-2,-2.75,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[2,9,0.1]);
currentColor = [0.2,0.2,0.2,1];
cube.render();
popMatrix();
//Road
pushMatrix();
mat4.translate(modelview,modelview,[-2,-2.72,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.2,9,0.1]);
currentColor = [1,1,1,1];
cube.render();
popMatrix();
//Basketball
pushMatrix();
mat4.translate(modelview,modelview,[xval,yval,3]);//
mat4.scale(modelview,modelview,[0.15,0.15,0.15]);
currentColor = [1,0.3,0,1];
sphere.render();
popMatrix();
// BasketBall Player
// Player 1
pushMatrix();
mat4.translate(modelview,modelview,[1,-2.2,3]);
mat4.scale(modelview,modelview,[0.15,0.15,0.15]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[0,1,0]);
// Arms
pushMatrix();
mat4.translate(modelview,modelview,[0,-0.5,-2]);
mat4.rotate(modelview,modelview,(-45)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.5,3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-2,-0.5,-2]);
mat4.rotate(modelview,modelview,(-45)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.5,3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
player();
popMatrix();
// PLayer 2
pushMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[2.4,-2.2,3.2]);
mat4.scale(modelview,modelview,[0.2,0.2,0.2]);
mat4.rotate(modelview,modelview,(-90)/180*Math.PI,[0,1,0]);
// Arms
pushMatrix();
mat4.translate(modelview,modelview,[0,1.2,-3]);
mat4.rotate(modelview,modelview,(kval)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.5,3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-2,1.2,-3]);
mat4.rotate(modelview,modelview,(kval)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.5,3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
player();
popMatrix();
//baskethoop
pushMatrix();
mat4.translate(modelview,modelview,[3,-4.3,3]);
mat4.scale(modelview,modelview,[0.5,0.5,0.5]);
basketballhoop();
popMatrix();
//Houses
pushMatrix();
mat4.translate(modelview,modelview,[-4,-2,2]);
mat4.scale(modelview,modelview,[0.6,0.6,0.6]);
house();
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-4,-2,-2.3]);
mat4.scale(modelview,modelview,[0.6,0.6,0.6]);
house();
popMatrix();
}
function streetLight(){
pushMatrix();
mat4.translate(modelview,modelview,[0,0,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[-1,0,0]);
mat4.scale(modelview,modelview,[0.2,0.2,4]);
cylinder.render();
popMatrix();
}
function house(){
pushMatrix();
currentColor = [0.5,0.4,0.2,1];
mat4.scale(modelview,modelview,[2,2,2]);
cube.render();
popMatrix();
pushMatrix();
currentColor = [0.5,0.4,0.2,1];
mat4.translate(modelview,modelview,[0,-0.5,1.6]);
mat4.scale(modelview,modelview,[1.3,1.3,1.3]);
cube.render();
popMatrix();
pushMatrix();
currentColor = [0.2,0.2,0.2,1];
mat4.translate(modelview,modelview,[0,1,0.0]);
mat4.scale(modelview,modelview,[4,1.5,4]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[-1,0,0]);
cone.render();
popMatrix();
}
function basketballhoop(){
pushMatrix();
currentColor = [0.1,0.1,0.1,1];
mat4.translate(modelview,modelview,[0,0,0]);
mat4.scale(modelview,modelview,[0.2,3,0.2]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[-1,0,0]);
cylinder.render();
popMatrix();
pushMatrix();
currentColor = [0.5,0.5,0.5,1];
mat4.scale(modelview,modelview,[0.15,0.15,0.15]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[-1,0,0]);
mat4.translate(modelview,modelview,[-4.5,0,43]);
ring.render();
popMatrix();
pushMatrix();
currentColor = [0,0,0.5,1];
mat4.scale(modelview,modelview,[0.1,1.5,2]);
mat4.translate(modelview,modelview,[0,4.4,0]);
cube.render();
popMatrix();
}
function player(){
pushMatrix();
//Head
mat4.translate(modelview,modelview,[-1,3,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.4,0.8]);
currentColor = [0.7,0.4,0.1,1];
sphere.render();
popMatrix();
// Neck
pushMatrix();
mat4.translate(modelview,modelview,[-1,2.5,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.3,0.4,0.3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
// Body
pushMatrix();
mat4.translate(modelview,modelview,[-1,1.0,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[1.3,1,2]);
currentColor = [0.0,0.0,0.9,1];
cube.render();
popMatrix();
// Legs
pushMatrix();
mat4.translate(modelview,modelview,[-1.3,3.1,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.6,0.6,3.0]);
currentColor = [0.0,0.0,1,1];
cylinder.render();
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-0.7,3.1,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.6,0.6,3.0]);
currentColor = [0.0,0.0,1,1];
cylinder.render();
popMatrix();
}
/**
* Push a copy of the current modelview matrix onto the matrix stack.
*/
function pushMatrix() {
matrixStack.push( mat4.clone(modelview) );
}
/**
* Restore the modelview matrix to a value popped from the matrix stack.
*/
function popMatrix() {
modelview = matrixStack.pop();
}
<!-- As is no changes -->
/**
* Create one of the basic objects. The modelData holds the data for
* an IFS using the structure from basic-objects-IFS.js. This function
* creates VBOs to hold the coordinates, normal vectors, and indices
* from the IFS, and it loads the data into those buffers. The function
* creates a new object whose properties are the identifies of the
* VBOs. The new object also has a function, render(), that can be called to
* render the object, using all the data from the buffers. That object
* is returned as the value of the function. (The second parameter,
* xtraTranslate, is there because this program was ported from a Java
* version where cylinders were created in a different position, with
* the base on the xy-plane instead of with their center at the origin.
* The xtraTranslate parameter is a 3-vector that is applied as a
* translation to the rendered object. It is used to move the cylinders
* into the position expected by the code that was ported from Java.)
*/
function createModel(modelData, xtraTranslate) {
var model = {};
model.coordsBuffer = gl.createBuffer();
model.normalBuffer = gl.createBuffer();
model.indexBuffer = gl.createBuffer();
model.count = modelData.indices.length;
if (xtraTranslate)
model.xtraTranslate = xtraTranslate;
else
model.xtraTranslate = null;
gl.bindBuffer(gl.ARRAY_BUFFER, model.coordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, modelData.vertexPositions, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, model.normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, modelData.vertexNormals, gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, model.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, modelData.indices, gl.STATIC_DRAW);
model.render = function() { // This function will render the object.
// Since the buffer from which we are taking the coordinates and normals
// change each time an object is drawn, we have to use gl.vertexAttribPointer
// to specify the location of the data. And to do that, we must first
// bind the buffer that contains the data. Similarly, we have to
// bind this object's index buffer before calling gl.drawElements.
gl.bindBuffer(gl.ARRAY_BUFFER, this.coordsBuffer);
gl.vertexAttribPointer(a_coords_loc, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);
gl.vertexAttribPointer(a_normal_loc, 3, gl.FLOAT, false, 0, 0);
gl.uniform4fv(u_material.diffuseColor, currentColor);
if (this.xtraTranslate) {
pushMatrix();
mat4.translate(modelview,modelview,this.xtraTranslate);
}
gl.uniformMatrix4fv(u_modelview, false, modelview );
mat3.normalFromMat4(normalMatrix, modelview);
gl.uniformMatrix3fv(u_normalMatrix, false, normalMatrix);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.drawElements(gl.TRIANGLES, this.count, gl.UNSIGNED_SHORT, 0);
if (this.xtraTranslate) {
popMatrix();
}
}
return model;
}
/* Creates a program for use in the WebGL context gl, and returns the
* identifier for that program. If an error occurs while compiling or
* linking the program, an exception of type String is thrown. The error
* string contains the compilation or linking error. If no error occurs,
* the program identifier is the return value of the function.
* The second and third parameters are the id attributes for <script>
* elementst that contain the source code for the vertex and fragment
* shaders.
*/
function createProgram(gl, vertexShaderID, fragmentShaderID) {
function getTextContent( elementID ) {
// This nested function retrieves the text content of an
// element on the web page. It is used here to get the shader
// source code from the script elements that contain it.
var element = document.getElementById(elementID);
var node = element.firstChild;
var str = "";
while (node) {
if (node.nodeType == 3) // this is a text node
str += node.textContent;
node = node.nextSibling;
}
return str;
}
try {
var vertexShaderSource = getTextContent( vertexShaderID );
var fragmentShaderSource = getTextContent( fragmentShaderID );
}
catch (e) {
throw "Error: Could not get shader source code from script elements.";
}
var vsh = gl.createShader( gl.VERTEX_SHADER );
gl.shaderSource(vsh,vertexShaderSource);
gl.compileShader(vsh);
if ( ! gl.getShaderParameter(vsh, gl.COMPILE_STATUS) ) {
throw "Error in vertex shader: " + gl.getShaderInfoLog(vsh);
}
var fsh = gl.createShader( gl.FRAGMENT_SHADER );
gl.shaderSource(fsh, fragmentShaderSource);
gl.compileShader(fsh);
if ( ! gl.getShaderParameter(fsh, gl.COMPILE_STATUS) ) {
throw "Error in fragment shader: " + gl.getShaderInfoLog(fsh);
}
var prog = gl.createProgram();
gl.attachShader(prog,vsh);
gl.attachShader(prog, fsh);
gl.linkProgram(prog);
if ( ! gl.getProgramParameter( prog, gl.LINK_STATUS) ) {
throw "Link error in program: " + gl.getProgramInfoLog(prog);
}
return prog;
}
/* Initialize the WebGL context. Called from init() */
function initGL() {
var prog = createProgram(gl,"vshader-source","fshader-source");
gl.useProgram(prog);
gl.enable(gl.DEPTH_TEST);
/* Get attribute and uniform locations */
a_coords_loc = gl.getAttribLocation(prog, "a_coords");
a_normal_loc = gl.getAttribLocation(prog, "a_normal");
gl.enableVertexAttribArray(a_coords_loc);
gl.enableVertexAttribArray(a_normal_loc);
u_modelview = gl.getUniformLocation(prog, "modelview");
u_projection = gl.getUniformLocation(prog, "projection");
u_normalMatrix = gl.getUniformLocation(prog, "normalMatrix");
u_material = {
diffuseColor: gl.getUniformLocation(prog, "material.diffuseColor"),
specularColor: gl.getUniformLocation(prog, "material.specularColor"),
emissiveColor: gl.getUniformLocation(prog, "material.emissiveColor"),
specularExponent: gl.getUniformLocation(prog, "material.specularExponent")
};
u_lights = new Array(4);
for (var i = 0; i < 4; i++) {
u_lights[i] = {
enabled: gl.getUniformLocation(prog, "lights[" + i + "].enabled"),
position: gl.getUniformLocation(prog, "lights[" + i + "].position"),
color: gl.getUniformLocation(prog, "lights[" + i + "].color")
};
}
gl.uniform3f( u_material.specularColor, 0.1, 0.1, 0.1 ); // specular properties don't change
gl.uniform1f( u_material.specularExponent, 16 );
gl.uniform3f( u_material.emissiveColor, 0, 0, 0); // default, will be changed temporarily for some objects
for (var i = 1; i < 4; i++) { // set defaults for lights
gl.uniform1i( u_lights[i].enabled, 0 );
gl.uniform4f( u_lights[i].position, 0, 0, 1, 0 );
gl.uniform3f( u_lights[i].color, 1,1,1 );
}
// Lights are set on in the draw() method
} // end initGL()
//--------------------------------- animation framework ------------------------------
var animating = false;
/*
This is where you control the animation by changing positions,
and rotations values as needed.
Trial and error works on the numbers. Graph paper design is more efficient.
*/
function frame() {
if (animating) {
frameNumber += 1;
// Positions of the soccer ball
xval += 0.05;
yval += 0.05;
// Rotation of kick leg
// Not perfect by anymeans but Okay for at least one viewing angle.
kval -= 1.5;
if (xval > 8.5 || yval < -8.5) {
xval= 1.3;
yval= -1.8;
kval = 0;
}
if (kval < -90) {
kval = -90; // Don't overextend the soccer all
}
draw();
requestAnimationFrame(frame);
}
}
function setAnimating(run) {
if (run != animating) {
animating = run;
if (animating)
requestAnimationFrame(frame);
}
}
//-------------------------------------------------------------------------
/**
* initialization function that will be called when the page has loaded
*/
function init() {
try {
var canvas = document.getElementById("webglcanvas");
gl = canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl");
if ( ! gl ) {
throw "Browser does not support WebGL";
}
}
catch (e) {
document.getElementById("message").innerHTML =
"<p>Sorry, could not get a WebGL graphics context.</p>";
return;
}
try {
initGL(); // initialize the WebGL graphics context
}
catch (e) {
document.getElementById("message").innerHTML =
"<p>Sorry, could not initialize the WebGL graphics context:" + e + "</p>";
return;
}
document.getElementById("animCheck").checked = false;
document.getElementById("reset").onclick = function() {
rotator.setView(17,[0,1,2]);
frameNumber = 0;
// Initialize soccer ball positions
xval = 1.5;
yval = -1.5;
animating = false;
document.getElementById("animCheck").checked = false;
draw();
}
// Not really using all of these
// As you create your scene use these or create from primitives
torus = createModel(uvTorus(0.5,1,16,8)); // Create all the basic objects.
sphere = createModel(uvSphere(1));
cone = createModel(uvCone(),[0,0,.5]);
cylinder = createModel(uvCylinder(),[0,0,1.5]);
disk = createModel(uvCylinder(5.5,0.5,64),[0,0,.25]);
ring = createModel(ring(3.3,4.8,40));
cube = createModel(cube());
// This controls the zoom and initial placement
rotator = new TrackballRotator(canvas,function() {
if (!animating)
draw();
},17,[0,1,2]);
draw();
}
</script>
</head>

Related

How can I delete a specific object in Webgl (Without library)

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 texture map a sphere in WebGL

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;
}

How do people pass functions to OpenGL ES GLSL functions?

I want to rotate and translate a 2d shape made with signed distance functions.
The docs say this is the method:
vec3 opTx( in vec3 p, in transform t, in sdf3d primitive )
{
return primitive( invert(t)*p );
}
It looks to me like primitive is some kind of function (or a struct) I can call, Is there a way to pass functions like that (or how does this make sense)?
Firstly I don't know what transform and sdf3d types are, and what is the invert function. Secondly how do I apply this to 2d?
const fShaderSource = `#version 300 es
precision mediump float;
uniform vec2 u_resolution;
out vec4 outColor;
float sdLine( in vec2 p, in vec2 a, in vec2 b )
{
vec2 pa = p-a, ba = b-a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
return length( pa - ba*h );
}
vec2 screenToWorld(vec2 screen) {
vec2 result = 2.0 * (screen/u_resolution.xy - 0.5);
result.x *= u_resolution.x/u_resolution.y;
return result;
}
void main() {
vec2 p = screenToWorld(gl_FragCoord.xy);
float sd = sdLine(p, vec2(0.0), vec2(0.0, 0.5));
vec3 col = vec3(0.0);
col += 1.0 - smoothstep(0.0, 0.04, abs(sd));
outColor = vec4(col, 1.0);
}
`;
const vShaderSource = `#version 300 es
precision mediump float;
in vec2 a_position;
uniform vec2 u_resolution;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
`;
main(document.getElementById('app'));
function main(element) {
const canvas = document.createElement('canvas'),
gl = canvas.getContext('webgl2');
element.append(canvas);
const displayWidth = canvas.clientWidth,
displayHeight = canvas.clientHeight;
canvas.width = displayWidth;
canvas.height = displayHeight;
let graphics = new Graphics({width: displayWidth, height: displayHeight}, gl);
new Loop(() => {
graphics.render();
}).start();
}
function Graphics(state, gl) {
const { width, height } = state;
let vShader = createShader(gl, gl.VERTEX_SHADER, vShaderSource);
let fShader = createShader(gl, gl.FRAGMENT_SHADER, fShaderSource);
let program = createProgram(gl, vShader, fShader);
let posAttrLocation = gl.getAttribLocation(program, "a_position");
let posBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
/*
(-1, 1).( 1, 1)
.
(-1,-1).( 1,-1)
*/
let positions = [
-1, 1,
-1, -1,
1, -1,
-1, 1,
1,-1,
1, 1
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
let vao = gl.createVertexArray();
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(posAttrLocation);
let size = 2,
type = gl.FLOAT,
normalize = false,
stride = 0,
offset = 0;
gl.vertexAttribPointer(posAttrLocation,
size,
type,
normalize,
stride,
offset);
let resUniformLocation = gl.getUniformLocation(program, "u_resolution");
gl.clearColor(0, 0, 0, 0);
this.render = () => {
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.uniform2f(resUniformLocation, gl.canvas.width, gl.canvas.height);
gl.bindVertexArray(vao);
gl.drawArrays(gl.TRIANGLES, 0, 6);
};
}
function createShader(gl, type, source) {
let shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (success) {
return shader;
}
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
};
function createProgram(gl, vShader, fShader) {
let program = gl.createProgram();
gl.attachShader(program, vShader);
gl.attachShader(program, fShader);
gl.linkProgram(program);
let success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (success) {
return program;
}
console.error(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
return null;
}
// Loop Library
function Loop(fn) {
const perf = window.performance !== undefined ? window.performance : Date;
const now = () => perf.now();
const raf = window.requestAnimationFrame;
let running = false,
lastUpdate = now(),
frame = 0;
this.start = () => {
if (running) {
return this;
}
running = true;
lastUpdate = now();
frame = raf(tick);
return this;
};
this.stop = () => {
running = false;
if (frame != 0) {
raf.cancel(frame);
}
frame = 0;
return this;
};
const tick = () => {
frame = raf(tick);
const time = now();
const dt = time - lastUpdate;
fn(dt);
lastUpdate = time;
};
}
#app canvas {
position: fixed;
top: 50%;
bottom: 0;
left: 50%;
right: 0;
width: 100vmin;
height: 70vmin;
transform: translate(-50%, -25%);
image-rendering: optimizeSpeed;
cursor: none;
margin: auto;
}
<div id="app">
</div>
GLSL does not allow you to pass functions as parameters. The snippet you linked is more of a macro, where you are supposed to manually inline the primitive.
Just above the code you copy-pasted, the definition of transform is stated:
This code bellow assumes that transform encodes only a rotation and a translation (as a 3x4 matrix for example, or as a quaternion and a vector), and that it does not contain any scaling factors in it.
To work in 2D, you use 3x3 matrices, where the upper 2x2 matrix encodes a rotation and the two first columns of the bottom row encode a translation.
Putting it all together: (replace the mainImage function of https://www.shadertoy.com/view/MldcD7 with this)
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
p *= 1.1;
// iFrame is a uniform that shadertoy provides: the current frame number
float angle = float(iFrame) / 60.0;
// Rotation part: rotate by `angle`, or once every 60 fps.
// Translation part: Move across the screen left to right, every 60 fps.
mat3 transform = mat3(
cos(angle), sin(angle), 0.0,
-sin(angle),cos(angle), 0.0,
(float(iFrame % 60)/60.0 - 0.5) * 2.0, 0.0, 1.0
);
vec2 tri = vec2(0.3,-1.1); // width, height
// Here, we first apply the inverse transform to our input, then pass the resulting point to our primitive, here sdTriangleIsosceles
float d = sdTriangleIsosceles( tri, (inverse(transform) * vec3(p, 1.0)).xy );
vec3 col = vec3(1.0) - sign(d)*vec3(0.1,0.4,0.7);
col *= 1.0 - exp(-2.0*abs(d));
col *= 0.8 + 0.2*cos(140.0*d);
col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.02,abs(d)) );
fragColor = vec4(col*1.2,1.0);
}

WebGL Resetting vertex positions

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>

Mandelbrot set zoom limit

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

Categories