I am able to load RGB colours but not textures. If it could be some settings problem please prompt me.
This is the screenshot of chrome://flags
The HTML code is given :
<!DOCTYPE html>
<meta charset="UTF-8">
<html>
<head>
<title>WebGL Cube with Texture</title>
<script type="x-shader/x-vertex" id="vshader">
attribute vec3 coords;
attribute vec2 texCoords;
uniform vec3 normal;
uniform mat4 modelview;
uniform mat4 projection;
uniform mat3 normalMatrix;
varying vec3 vNormal;
varying vec2 vTexCoords;
void main() {
vec4 coords = vec4(coords,1.0);
vec4 transformedVertex = modelview * coords;
vNormal = normalMatrix * normal;
vTexCoords = texCoords;
gl_Position = projection * transformedVertex;
}
</script>
<script type="x-shader/x-fragment" id="fshader">
precision mediump float;
uniform bool textured;
uniform sampler2D sampler;
varying vec3 vNormal;
varying vec2 vTexCoords;
uniform vec4 color;
void main() {
if (textured) {
vec4 color = texture2D(sampler, vTexCoords);
vec3 unitNormal = normalize(vNormal);
float multiplier = abs(unitNormal.z);
gl_FragColor = vec4( multiplier*color.r, multiplier*color.g, multiplier*color.b, color.a );
}
else {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); // use basic white when texture's not there.
}
}
</script>
<script type="text/javascript" src="gl-matrix-min.js"></script>
<script type="text/javascript" src="simple-rotator.js"></script>
<script type="text/javascript">
"use strict";
var gl; // The webgl context.
var aCoords; // Location of the coords attribute variable in the shader program.
var aCoordsBuffer; // Buffer to hold coords.
var aTexCoords; // Location of the texCoords attribute variable in the shader program.
var aTexCoordsBuffer; // Buffer to hold texCoords.
var uProjection; // Location of the projection uniform matrix in the shader program.
var uModelview; // Location of the modelview unifirm matrix in the shader program.
var uNormal; // Location of the normal uniform in the shader program.
var uColor; // Location of the color uniform in the shader program, used only for axes.
var uTextured; // Location of the textured uniform in the shader program.
var uSampler; // Location of the sampler in the shader program.
var uNormalMatrix; // Location of the normalMatrix uniform matrix in the shader program.
var projection = mat4.create(); // projection matrix
var modelview = mat4.create(); // modelview matrix
var normalMatrix = mat3.create(); // matrix, derived from modelview matrix, for transforming normal vectors
var rotator; // A SimpleRotator object to enable rotation by mouse dragging.
var textureID = null; // Texture object, to be created after image has loaded.
/* Draws a colored cube, along with a set of coordinate axes.
* (Note that the use of the above drawPrimitive function is not an efficient
* way to draw with WebGL. Here, the geometry is so simple that it doesn't matter.)
*/
function draw() {
gl.clearColor(0,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
if (document.getElementById("persproj").checked) {
mat4.perspective(projection, Math.PI/4, 1, 2, 10);
}
else {
mat4.ortho(projection,-2.5, 2.5, -2.5, 2.5, 2, 10);
}
gl.uniformMatrix4fv(uProjection, false, projection );
var modelview = rotator.getViewMatrix();
var saveModelview = mat4.clone(modelview);
if (textureID) {
gl.uniform1i( uTextured, 1 ); // Tell shader to use texture and lighting.
gl.bindTexture(gl.TEXTURE_2D, textureID); // Which texture should be used.
gl.uniform1i(uSampler, 0); // Set sampler in shadre to use texture unit zero.
}
else {
gl.uniform1i( uTextured, 0 ); // Cube will appear in plain white.
}
drawFace(modelview) // front face of the cube
mat4.rotateY(modelview,modelview,Math.PI/2); //right face
drawFace(modelview) // front face
mat4.rotateY(modelview,modelview,Math.PI/2); //back face
drawFace(modelview) // front face
mat4.rotateY(modelview,modelview,Math.PI/2); //left face
drawFace(modelview) // front face
modelview = mat4.clone(saveModelview);
mat4.rotateX(modelview,modelview,Math.PI/2);
drawFace(modelview) // top face
mat4.rotateX(modelview,modelview,Math.PI);
drawFace(modelview) // bottom face
}
/**
* Draws the front face of the cube, subject to a modelview transform.
*/
function drawFace(modelview) {
gl.uniformMatrix4fv(uModelview, false, modelview );
mat3.normalFromMat4(normalMatrix, modelview);
gl.uniformMatrix3fv(uNormalMatrix, false, normalMatrix);
gl.uniform3f(uNormal, 0, 0, 1);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4); // front face
}
/**
* Loads data for the front face of the cube into VBOs.
*/
function createFace() {
var vertices = [ -1,-1,1, 1,-1,1, 1,1,1, -1,1,1 ];
var texCoords = [ 0,0, 2,0, 2,2, 0,2 ];
gl.enableVertexAttribArray(aCoords);
gl.bindBuffer(gl.ARRAY_BUFFER,aCoordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(aCoords, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aTexCoords);
gl.bindBuffer(gl.ARRAY_BUFFER,aTexCoordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);
gl.vertexAttribPointer(aTexCoords, 2, gl.FLOAT, false, 0, 0);
}
/**
* Load an image from the URL "textures/bridk001.jpg". The image is loade
* asynchronously. When the
*/
function loadTexture() {
var img = new Image();
img.onload = function() {
var id = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D,id);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
textureID = id;
draw();
}
img.src = "./skin.jpg";
}
/* 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.
*/
function createProgram(gl, vertexShaderSource, fragmentShaderSource) {
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;
}
/* Gets the text content of an HTML element. This is used
* to get the shader source from the script elements that contain
* it. The parameter should be the id of the script element.
*/
function getTextContent( elementID ) {
var element = document.getElementById(elementID);
var fsource = "";
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;
}
/**
* Initializes the WebGL program including the relevant global variables
* and the WebGL state. Creates a SimpleView3D object for viewing the
* cube and installs a mouse handler that lets the user rotate the cube.
*/
function init() {
try {
var canvas = document.getElementById("glcanvas");
gl = canvas.getContext("webgl");
if ( ! gl ) {
gl = canvas.getContext("experimental-webgl");
}
if ( ! gl ) {
throw "Could not create WebGL context.";
}
var vertexShaderSource = getTextContent("vshader");
var fragmentShaderSource = getTextContent("fshader");
var prog = createProgram(gl,vertexShaderSource,fragmentShaderSource);
gl.useProgram(prog);
aCoords = gl.getAttribLocation(prog, "coords");
aTexCoords = gl.getAttribLocation(prog, "texCoords");
uModelview = gl.getUniformLocation(prog, "modelview");
uProjection = gl.getUniformLocation(prog, "projection");
uSampler = gl.getUniformLocation(prog, "sampler");
uNormal = gl.getUniformLocation(prog, "normal");
uColor = gl.getUniformLocation(prog, "color");
uTextured = gl.getUniformLocation(prog, "textured");
uNormalMatrix = gl.getUniformLocation(prog, "normalMatrix");
aCoordsBuffer = gl.createBuffer();
aTexCoordsBuffer = gl.createBuffer();
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE); // no need to draw back faces
document.getElementById("persproj").checked = true;
rotator = new SimpleRotator(canvas,draw);
rotator.setView( [2,2,5], [0,1,0], 6 );
}
catch (e) {
document.getElementById("message").innerHTML =
"Could not initialize WebGL: " + e;
return;
}
createFace();
loadTexture();
draw();
}
</script>
</head>
<body onload="init()" style="background-color:#DDD">
<h2>A Cube With a Brick Texture</h2>
<p id=message>Drag the mouse on the canvas to rotate the view.</p>
<p>
<input type="radio" name="projectionType" id="persproj" value="perspective" onchange="draw()">
<label for="persproj">Perspective projection</label>
<input type="radio" name="projectionType" id="orthproj" value="orthogonal" onchange="draw()" style="margin-left:1cm">
<label for="orthproj">Orthogonal projection</label>
<button onclick="rotator.setView( [2,2,5], [0,1,0], 6 ); draw()" style="margin-left:1cm">Reset View</button>
</p>
<noscript><hr><h3>This page requires Javascript and a web browser that supports WebGL</h3><hr></noscript>
<div>
<canvas width=600 height=600 id="glcanvas" style="background-color:red"></canvas>
</div>
</body>
</html>
All i get as an output is
The other functions are loading fine. The file paths are correct.
The issue is you need to run a simple web server for WebGL dev. It should take you about 2 minutes to get setup
See this
Try defining the minification and magnification parameters for the texture object.
eg:
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
Use the appropriate value for min and mag filter, based on your project requirement.
Related
Dear Firend : I am new at WebGl. I have managed to draw and rotate a triangle
-- but the problem is that the triangle
1, The triangle change size and shape while drawing.
2, I dont know how to rotate the triangle around one of its cornor.
Following is the code. I have written the code in a linear fashion with a utility class (GlUtil) that wraps up the boring tasks.
I am using a function called perc2glCoord that allow me to enter percentages and convert them into gl coordinates.
import GlUtil from "./glUtil/glUtil.js";
import perc2glCoord from "./functions/perc2glCoord.js";
const gl = GlUtil.getGl("bilza");
console.log(gl);
const vertices = [
perc2glCoord (50) ,perc2glCoord (50), 1,0,0,
perc2glCoord (50) ,perc2glCoord (75), 0,1,0,
perc2glCoord (75) ,perc2glCoord (50), 0,0,1,
// perc2glCoord (25) ,perc2glCoord (50), 1,0,0,
// perc2glCoord (75) ,perc2glCoord (50), 0,1,0,
// perc2glCoord (50) ,perc2glCoord (75), 0,0,1,
];
const vertexShaderSrc =
`
attribute highp vec2 a_pos;
attribute highp vec3 a_clr;
uniform float translateX;
uniform float translateY;
uniform float angle;
varying highp vec3 vColor;
void main(void) {
gl_Position = vec4(
translateX + (a_pos.x * cos(angle) - a_pos.y * sin(angle)),
translateY + (a_pos.x * sin(angle) + a_pos.y * cos(angle)),
1.0,
1.0 );
vColor = a_clr;
}
`;
const fragShaderSrc =
`
varying highp vec3 vColor;
void main(void) {
gl_FragColor = vec4 (vColor , 1.0);
}
`;
const vertexShader = GlUtil.createShader(gl,vertexShaderSrc,gl.VERTEX_SHADER);
const fragmentShader = GlUtil.createShader(gl,fragShaderSrc,gl.FRAGMENT_SHADER);
const programe = GlUtil.getProgram(gl,vertexShader,fragmentShader);
const VOB = GlUtil.getBuffer(gl);
GlUtil.bindBuffer(gl,VOB,vertices);
GlUtil.linkNuseProgram(gl,programe);
let angleValue = 0;
function draw(){
GlUtil.setAttribute(gl,"a_pos",programe, 2 ,4*5,0);
GlUtil.setAttribute(gl,"a_clr",programe, 3 , 4*5,2 * 4);
const translateXLoc = gl.getUniformLocation(programe, "translateX");
gl.uniform1f(translateXLoc,0.0);
const translateYLoc = gl.getUniformLocation(programe, "translateY");
gl.uniform1f(translateYLoc,0.0);
const angleLoc = gl.getUniformLocation(programe, "angle");
const rands = Math.PI * angleValue /180;
gl.uniform1f(angleLoc,rands);
angleValue+= 0.1;
/////////////////////---draw-----------------
GlUtil.clear(gl,0.1,0.1,0.2);
gl.drawArrays(gl.TRIANGLES , 0, 3);
requestAnimationFrame(draw);
}
draw();
---
Here is the GlUtil helper object
export default class GlUtil {
static getGl(canvasId :string ="bilza"):WebGLRenderingContext{
const canvas = document.getElementById(canvasId) as HTMLCanvasElement;
if (canvas == null){
throw new Error("canvas not found");
}
const gl = canvas.getContext("webgl");
if (gl == null) {
throw new Error("Unable to initialize WebGL. Your browser or machine may not support it.");
}
//---Got gl
return gl;
}
static getProgram(gl :WebGLRenderingContext,vshader:WebGLShader, fshader :WebGLShader) :WebGLProgram {
const pgm = gl.createProgram();
if (pgm == null){throw new Error("failed to create program");}
//-----------
gl.attachShader(pgm, vshader);
gl.attachShader(pgm, fshader);
//-------------
// pgm.vertexPosAttrib = gl.getAttribLocation( pgm , 'pos');
// this.gl.useProgram(this.program);
return pgm;
}
static getBuffer(gl :WebGLRenderingContext):WebGLBuffer{
let b = gl.createBuffer();
if (b == null){throw("failed to create buffer");}
return b;
}
static createShader(gl :WebGLRenderingContext, shaderSource :string, shaderType:number):WebGLShader {
var shader = gl.createShader(shaderType);
if (shader == null){
throw new Error("shaders could not be created");
}
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
let compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
// There are errors, so display them
var errors = gl.getShaderInfoLog(shader);
console.log('Failed to compile with these errors:' + "type:" + shaderType, errors );
}
return shader;
}
static bindBuffer(gl :WebGLRenderingContext,buff :WebGLBuffer,buffData :number[]){
gl.bindBuffer(gl.ARRAY_BUFFER, buff);
gl.bufferData( gl.ARRAY_BUFFER, new Float32Array(buffData),
gl.STATIC_DRAW);
}
static linkNuseProgram(gl :WebGLRenderingContext,prgrm :WebGLProgram){
gl.linkProgram(prgrm);
gl.useProgram(prgrm);
}
static clear(gl :WebGLRenderingContext,r:number=0,g:number=0,b:number=0,a:number=1){
gl.clearColor(r,g,b,a);
gl.clear(gl.COLOR_BUFFER_BIT);
}
////////////////////////////////////////////////////
static setAttribute(gl :WebGLRenderingContext,nameStr :string,programe :WebGLProgram,numberOfComps :number,stride:number, offset :number=0){
const vertexPosAttrib = gl.getAttribLocation( programe, `${nameStr}`);
gl.enableVertexAttribArray( vertexPosAttrib);
gl.vertexAttribPointer(
vertexPosAttrib, //index
numberOfComps, //number of components =2 x and y
gl.FLOAT, //data type
false, //normalized
stride , //stride - the comple vertex row bytes
offset //offset = 0
);
}
///////////////////////////////////////////////
}
Anf finally here is the picture of the triangle
Tried all the examples and help that I could find on the internet including re-learning the math
Since GL's drawing-area is stretched to fit the viewport, you need to take into account aspect ratio of the view in gl_Position calculation.
For example, appending aspectRatio parameter to the vertex shader,
uniform float aspectRatio;
:
gl_Position = vec4(
(translateX + (a_pos.x * cos(angle) - a_pos.y * sin(angle))) / aspectRatio,
translateY + (a_pos.x * sin(angle) + a_pos.y * cos(angle)),
1.0,
1.0 );
pass the viewport aspect-ratio as follows.
const viewport = gl.getParameter(gl.VIEWPORT); // [x, y, width, height]
const aspectRatioLoc = gl.getUniformLocation(programe, "aspectRatio");
gl.uniform1f(aspectRatioLoc, viewport[2] / viewport[3]);
I'm currently trying to use a fragment shader to transport some particle position data (and in the future, modify it). I have no problem sending the data to the shader using a sampler2D texture, but when I try to recuperate the data, My 20 particles suddenly have the wrong positions. I've printed as many outputs as possible and have minimized the code as much as I can yet still fail to understand where I'm wrong.
a reproducible minimized version is available on the p5js website here
Here is my sketch.js :
let theShader;
let shaderTexture;
let NUM_PARTICLES = 20;
let particleTexture;
let particles = [];
function preload(){
theShader = loadShader('basic.vert', 'basic.frag');
}
function setup() {
createCanvas(400, 400, WEBGL);
// Initiate Particles
for(let i = 0; i < NUM_PARTICLES;i++){
particles.push(createVector(i*20,i*20,0));
}
// Initialize Shader
shaderTexture = createGraphics(NUM_PARTICLES, 1, WEBGL);
shaderTexture.noStroke();
// Create Particle Texture
particleTexture = createImage(NUM_PARTICLES, 1);
// Fill Particle Texture
particleTexture.loadPixels();
for (let i = 0; i < NUM_PARTICLES; i++) {
particleTexture.pixels[i*4+0] = map(particles[i].x,0,width,0,255); // R
particleTexture.pixels[i*4+1] = map(particles[i].y,0,height,0,255); // G
particleTexture.pixels[i*4+2] = 0; // B
particleTexture.pixels[i*4+3] = 255; // A
}
particleTexture.updatePixels();
}
function draw() {
translate(-width/2, -height/2);
background(255);
// Display Particles Before Modification
for(let i = 0; i < NUM_PARTICLES;i++){
circle(particles[i].x,particles[i].y,10); // draw circle at particle location
}
// Apply Texture
shaderTexture.shader(theShader); // set shader
theShader.setUniform('text', particleTexture); // send particleTexture to shader
shaderTexture.rect(0,0,NUM_PARTICLES,1); // set rect to recieve shader out
// Print Shader Output
for(let i = 0; i < NUM_PARTICLES;i++){
let newPos = shaderTexture.get(i, 0);
print(newPos);
}
// Update and Display Particles
for(let i = 0; i < NUM_PARTICLES;i++){
let newPos = shaderTexture.get(i, 0);
particles[i].x = map(newPos[0],0,255,0,width);
particles[i].y = map(newPos[1],0,255,0,height);
fill(255,0,0);
circle(particles[i].x,particles[i].y,10);
}
noLoop();
}
and here is my fragment shader which should not be modifying anything :
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D text;
void main() {
vec2 particle = texture2D(text, vec2(gl_FragCoord.x, gl_FragCoord.y)).xy;
gl_FragColor = vec4(particle.x,particle.y,0.0,1.0); // R,G,B,A
}
also my vertex shader which is default :
#ifdef GL_ES
precision highp float;
#endif
attribute vec3 aPosition;
void main() {
vec4 positionVec4 = vec4(aPosition, 1.0);
positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
gl_Position = positionVec4;
}
When looking up a texture with texture2D the texture coordinates must be specified in range [0.0, 1.0]. (0, 0) is the bottom left and (1, 1) is the top right. However gl_FragCoord.xy contains window coordinates, with top left (0.5, 0.5) and top right (width-0.5, height-0.5).
Hence you need to divide gl_FragCoord by the size of the viewport. Since you draw on a rectangle with the size NUM_PARTICLESx1 (width == NUM_PARTICLES, height = 1), you must divide gl_FragCoord.x by NUM_PARTICLES:
vec2 particle = texture2D(text, vec2(gl_FragCoord.x, gl_FragCoord.y)).xy;
vec2 particle = texture2D(text, vec2(gl_FragCoord.x/20.0, 0.0)).xy;
Alternatively you can add a size uniform to the fragment shader and divide gl_FragCoord.xy by size:
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D text;
uniform vec2 size;
void main() {
vec2 particle = texture2D(text, gl_FragCoord.xy / size).xy;
gl_FragColor = vec4(particle.x,particle.y,0.0,1.0); // R,G,B,A
}
Set the value of the size uniform by [NUM_PARTICLES, 1]:
// Apply Texture
shaderTexture.shader(theShader); // set shader
theShader.setUniform('text', particleTexture); // send particleTexture to shader
theShader.setUniform('size', [NUM_PARTICLES, 1]);
shaderTexture.rect(0,0,NUM_PARTICLES,1); // set rect to recieve shader out
If you are using OpenGL ES Shading Language 3.00 you have the option to use texelFetch to lookup the texture with integral pixel coordinates or get the size of the texture using textureSize.
Unfortunately p5.js (createCanvas()) doesn't seem to provide a WebGL 2.0 context, so this is not an option.
I'm trying to do position updates for particles being rendered as points using webgl. I've asked some questions before about the same project I'm playing around with here and here which lead me a fair bit on the way. Unfortunately, most of the answers use twgl which, to me, takes a lot of shortcuts which I have a hard time understanding (so I didn't want to just try to copy it either but start with the basics).
Basically, I'm trying to render to a texture with one framebuffer + program and then use this texture in another program.
I don't know if I'm failing to render to the posTexture, of if the posTexture gets successfully rendered and that it fails to load into the renderProgram afterwards (since both are happening in the 'blackbox' GPU).
I made a snippet here without the renderFramebuffer (it simply renders directly to canvas instead) to show the problem. The core of the problem is at the end of the javascript bit, the rest is setup (which may be related):
function initShaderProgram(gl, vShader, fShader) {
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vShader);
gl.attachShader(shaderProgram, fShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
throw new Error('Unable to initiate webgl shaders. Breaking.');
}
return shaderProgram;
}
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
let err = gl.getShaderInfoLog(shader);
gl.deleteShader(shader);
throw new Error(`Unable to compile shaders. ${err}`);
}
return shader;
}
const c = document.getElementById("c");
const gl = c.getContext('webgl2');
const amParticles = 1;
if (gl === null || gl === undefined) {
throw new Error('Unable to initiate webgl context. Breaking.');
}
// Extensions used for anti aliasing in rendering dots
let ext = gl.getExtension('EXT_color_buffer_float');
if (!ext) {
throw new Error("need EXT_color_buffer_float");
}
ext = gl.getExtension('EXT_float_blend');
if (!ext) {
throw new Error("need EXT_float_blend");
}
// Setup programs
const VsPos = document.getElementById("posVs").textContent;
const FsPos = document.getElementById("posFs").textContent;
const VsRender = document.getElementById("renderVs").textContent;
const FsRender = document.getElementById("renderFs").textContent;
const vShaderRender = loadShader(gl,
gl.VERTEX_SHADER, VsRender);
const vShaderPosUpd = loadShader(gl,
gl.VERTEX_SHADER, VsPos);
const fShaderRender = loadShader(gl,
gl.FRAGMENT_SHADER, FsRender);
const fShaderPosUpd = loadShader(gl,
gl.FRAGMENT_SHADER, FsPos);
// Setup shader
const renderProgram = initShaderProgram(gl,
vShaderRender, fShaderRender);
const posProgram = initShaderProgram(gl,
vShaderPosUpd, fShaderPosUpd);
// Setup global GL settings
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Blending to allow opacity (probably unrelated)
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
// Setup posTexture to render new positions to
let posTexture, posFrameBuffer; {
posTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, posTexture);
// Make texture non-mips
gl.texParameteri(gl.TEXTURE_2D,
gl.TEXTURE_WRAP_S,
gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D,
gl.TEXTURE_WRAP_T,
gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D,
gl.TEXTURE_MIN_FILTER,
gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D,
gl.TEXTURE_MAG_FILTER,
gl.NEAREST);
const level = 0;
const internalFormat = gl.RGBA32F;
const border = 0;
const format = gl.RGBA;
const type = gl.FLOAT;
// Example position pre-render
const data = new Float32Array([.5, .5, 0, 0]);
// height = 1, amount pixels = width, rgba = position
gl.texImage2D(gl.TEXTURE_2D,
level,
internalFormat,
amParticles,
1,
border,
format,
type,
data);
// Pos framebuffer
posFrameBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER,
posFrameBuffer);
// Bind it to posTexture
gl.framebufferTexture2D(gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
posTexture,
level);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !==
gl.FRAMEBUFFER_COMPLETE) {
console.error(`Something went wrong with setting up the the posFrameBuffer. Status: ${
gl.checkFramebufferStatus(gl.FRAMEBUFFER)}`);
}
}
gl.useProgram(posProgram);
gl.bindFramebuffer(gl.FRAMEBUFFER, posFrameBuffer);
gl.viewport(0, 0, amParticles, 1);
// Now (it should be?) drawing new positions to
// texture posTexture
gl.drawArrays(gl.POINTS, 0, amParticles);
// Set new posTexture to texture unit 1
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, posTexture);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.useProgram(renderProgram);
// Set uniform location to texture unit 1
const loc = gl.getUniformLocation(renderProgram, "t0_pos_tex");
gl.uniform1i(loc, 1);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Should draw with new position
gl.drawArrays(gl.POINTS, 0, amParticles);
#c {
width: 400px;
height: 200px;
}
.hide {
display: none;
}
<canvas id="c"></canvas>
<p>
If the circle is at the <b>left</b> side of the canvas, rendered by the posProgram, the experiment is successfull.
</p>
<div id="posFs" class="hide"># version 300 es
#define M_PI 3.1415927
precision highp float;
out vec4 outColor;
// Only renders one particle for the sake
// of the example to a predetermined position
void main() {
// New position to render to (should appear
// top-left ish)
float new_x = -.5;
float new_y = .5;
outColor = vec4(new_x, new_y, 0., 1.);
}
</div>
<div id="posVs" class="hide">#version 300 es
// Does nothing since the fragment shader sets
// the new position depending on the pixel
// which indicates which index of the texture
// = index of the new positions to update
void main() {}
</div>
<div id="renderVs" class="hide"># version 300 es
#define M_PI 3.1415927
uniform sampler2D t0_pos_tex;
out vec4 color;
void main() {
vec4 t0_pos = texelFetch(t0_pos_tex, ivec2(gl_VertexID, 0), 0);
gl_Position = vec4(t0_pos.x, t0_pos.y, 0., 1.);
color = vec4(1., 1., 1., 1.);
gl_PointSize = 50.0;
}
</div>
<div id="renderFs" class="hide"># version 300 es
precision highp float;
in vec4 color;
out vec4 outColor;
// Turns point into a circle and adds
// antialiasing to make it smoothly round
void main() {
float r = 0.0, delta = 0.0, alpha = 1.0;
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
r = dot(cxy, cxy);
delta = fwidth(r);
alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r);
outColor = color * alpha;
}
</div>
Your posVS vertex shader does nothing so nothing will be rendered by the fragment shader. In order to render something the vertex shader must either generate a point by setting gl_Position and gl_PointSize OR it must generate a line by being called twice and setting gl_Position to different values each time or a triangle by being called 3 times and setting gl_Position to different values each time so that's the first issue.
changed it to this
void main() {
// draw a single pixel
gl_PointSize = 1.0;
// in the center of the viewport
gl_Position = vec4(0, 0, 0, 1);
}
function initShaderProgram(gl, vShader, fShader) {
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vShader);
gl.attachShader(shaderProgram, fShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
throw new Error('Unable to initiate webgl shaders. Breaking.');
}
return shaderProgram;
}
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
let err = gl.getShaderInfoLog(shader);
gl.deleteShader(shader);
throw new Error(`Unable to compile shaders. ${err}`);
}
return shader;
}
const c = document.getElementById("c");
const gl = c.getContext('webgl2');
const amParticles = 1;
if (gl === null || gl === undefined) {
throw new Error('Unable to initiate webgl context. Breaking.');
}
// Extensions used for anti aliasing in rendering dots
let ext = gl.getExtension('EXT_color_buffer_float');
if (!ext) {
throw new Error("need EXT_color_buffer_float");
}
ext = gl.getExtension('EXT_float_blend');
if (!ext) {
throw new Error("need EXT_float_blend");
}
// Setup programs
const VsPos = document.getElementById("posVs").textContent;
const FsPos = document.getElementById("posFs").textContent;
const VsRender = document.getElementById("renderVs").textContent;
const FsRender = document.getElementById("renderFs").textContent;
const vShaderRender = loadShader(gl,
gl.VERTEX_SHADER, VsRender);
const vShaderPosUpd = loadShader(gl,
gl.VERTEX_SHADER, VsPos);
const fShaderRender = loadShader(gl,
gl.FRAGMENT_SHADER, FsRender);
const fShaderPosUpd = loadShader(gl,
gl.FRAGMENT_SHADER, FsPos);
// Setup shader
const renderProgram = initShaderProgram(gl,
vShaderRender, fShaderRender);
const posProgram = initShaderProgram(gl,
vShaderPosUpd, fShaderPosUpd);
// Setup global GL settings
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Blending to allow opacity (probably unrelated)
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
// Setup posTexture to render new positions to
let posTexture, posFrameBuffer; {
posTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, posTexture);
// Make texture non-mips
gl.texParameteri(gl.TEXTURE_2D,
gl.TEXTURE_WRAP_S,
gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D,
gl.TEXTURE_WRAP_T,
gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D,
gl.TEXTURE_MIN_FILTER,
gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D,
gl.TEXTURE_MAG_FILTER,
gl.NEAREST);
const level = 0;
const internalFormat = gl.RGBA32F;
const border = 0;
const format = gl.RGBA;
const type = gl.FLOAT;
// Example position pre-render
const data = new Float32Array([.5, .5, 0, 0]);
// height = 1, amount pixels = width, rgba = position
gl.texImage2D(gl.TEXTURE_2D,
level,
internalFormat,
amParticles,
1,
border,
format,
type,
data);
// Pos framebuffer
posFrameBuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER,
posFrameBuffer);
// Bind it to posTexture
gl.framebufferTexture2D(gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
posTexture,
level);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !==
gl.FRAMEBUFFER_COMPLETE) {
console.error(`Something went wrong with setting up the the posFrameBuffer. Status: ${
gl.checkFramebufferStatus(gl.FRAMEBUFFER)}`);
}
}
gl.useProgram(posProgram);
gl.bindFramebuffer(gl.FRAMEBUFFER, posFrameBuffer);
gl.viewport(0, 0, amParticles, 1);
// Now (it should be?) drawing new positions to
// texture posTexture
gl.drawArrays(gl.POINTS, 0, amParticles);
// Set new posTexture to texture unit 1
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, posTexture);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.useProgram(renderProgram);
// Set uniform location to texture unit 1
const loc = gl.getUniformLocation(renderProgram, "t0_pos_tex");
gl.uniform1i(loc, 1);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Should draw with new position
gl.drawArrays(gl.POINTS, 0, amParticles);
#c {
width: 400px;
height: 200px;
}
.hide {
display: none;
}
<canvas id="c"></canvas>
<p>
If the circle is at the <b>left</b> side of the canvas, rendered by the posProgram, the experiment is successfull.
</p>
<div id="posFs" class="hide"># version 300 es
#define M_PI 3.1415927
precision highp float;
out vec4 outColor;
// Only renders one particle for the sake
// of the example to a predetermined position
void main() {
// New position to render to (should appear
// top-left ish)
float new_x = -.5;
float new_y = .5;
outColor = vec4(new_x, new_y, 0., 1.);
}
</div>
<div id="posVs" class="hide">#version 300 es
void main() {
// draw a single pixel
gl_PointSize = 1.0;
// in the center of the viewport
gl_Position = vec4(0, 0, 0, 1);
}
</div>
<div id="renderVs" class="hide"># version 300 es
#define M_PI 3.1415927
uniform sampler2D t0_pos_tex;
out vec4 color;
void main() {
vec4 t0_pos = texelFetch(t0_pos_tex, ivec2(gl_VertexID, 0), 0);
gl_Position = vec4(t0_pos.x, t0_pos.y, 0., 1.);
color = vec4(1., 1., 1., 1.);
gl_PointSize = 50.0;
}
</div>
<div id="renderFs" class="hide"># version 300 es
precision highp float;
in vec4 color;
out vec4 outColor;
// Turns point into a circle and adds
// antialiasing to make it smoothly round
void main() {
float r = 0.0, delta = 0.0, alpha = 1.0;
vec2 cxy = 2.0 * gl_PointCoord - 1.0;
r = dot(cxy, cxy);
delta = fwidth(r);
alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r);
outColor = color * alpha;
}
</div>
But, I suggest you spend a couple of minutes to try to understand the example linked. Yes it uses TWGL because the point of explaining how to do particles does not also want to be a tutorial on the entirely of WebGL. It should be pretty obvious what twgl.createTexture does just by looking at the inputs. Similarly with, twgl.createFramebufferInfo and twgl.createBufferInfoFromArrays if not obvious are probably just a few seconds away from understanding. twgl.setBuffersAndAttributes and twgl.setUniforms do exactly what they say. If you've done either of those things manually in webgl it should be pretty clear what it means to "set buffers and attributes" and to "set uniforms". All that's left is twgl.drawBufferInfo
In any case, it's going to be slower computing the new particle positions using gl.POINTS, one point per particle, rather than drawing a quad with N pixels, one for pixel for each point. Drawing the particles you might use gl.POINTS but not updating the positions.
One other note: like you print the shader info log when compiling fails you probably want to print the program info log when linking fails. There are plenty of errors that only happen during linking.
I'm working on a Javascript/OpenGL(WebGL) program that's supposed to make a 3D cube with a texture colored to look like a Rubik's cube. So far, I have the texture mapped just fine, and every point in the cube seems to be showing properly.
However, I'm having a tough time getting the color to display on it without getting some errors from the fragment shader. At the moment, I get this one error I've been trying to figure out with no luck. Here's the error message.
Error: fragment shader compiler: ERROR: 0:11: 'constructor' : too many arguments
Here's my fragment shader code to look at specifically.
<script id="fragment-shader" type="x-shader">
precision mediump float;
uniform sampler2D image0;
varying vec2 textureCoordinatesV;
varying vec4 pointColorV;
void main()
{
// extract the RGBA color from image at given coordinates.
vec4 oldColor = texture2D( image0, textureCoordinatesV );
vec4 newColor;
newColor = vec4(oldColor.r * pointColorV, oldColor.g * pointColorV, oldColor.b * pointColorV, 1.0);
gl_FragColor = newColor;
}
</script>
Here's the image I'm using for this program.
http://tinypic.com/view.php?pic=2rpbbqq&s=9#.Vkj3jPmrTIU
And here's my code in it's entirety. I am aware of three.js to render 3D objects in WebGL, but this current approach is for me to get a better understanding of how to render the object with existing tools. Also, if you're having trouble seeing the cube, try running this program in Firefox.
There is a Vector4.js and Matrix4.js I'm using with this program, but from what I understand, they don't have anything to do with the current error message I'm receiving. Vector4 is just a script that establishes a Vector4 structure for me to use to hold vertices to render, and Matrix4 is a script for moving those vertices once they're established. If need be, I will post these files in the comments.
Any and all help would be appreciated on figuring out why this error keeps coming up, thank you very much.
<html>
<head>
<title>Template WebGL file</title>
</head>
<body onload="main()">
<canvas id="myCanvas" width="400" height="400"></canvas>
<!-- Load external file with helper setup functions. -->
<script src="webgl-utils.js"></script>
<script src="Vector4.js"></script>
<script src="Matrix4.js"></script>
<!-- vertex shader code -->
<script id="vertex-shader" type="x-shader">
attribute vec4 pointPosition;
uniform mat4 transformation;
attribute vec4 pointColorA;
varying vec4 pointColorV;
attribute vec2 textureCoordinatesA;
varying vec2 textureCoordinatesV;
void main()
{
gl_Position = transformation * pointPosition;
textureCoordinatesV = textureCoordinatesA;
pointColorV = pointColorA;
}
</script>
<!-- fragment shader code -->
<script id="fragment-shader" type="x-shader">
precision mediump float;
uniform sampler2D image0;
varying vec2 textureCoordinatesV;
varying vec4 pointColorV;
void main()
{
// extract the RGBA color from image at given coordinates.
vec4 oldColor = texture2D( image0, textureCoordinatesV );
vec4 newColor;
newColor = vec4(oldColor.r * pointColorV, oldColor.g * pointColorV, oldColor.b * pointColorV, 1.0);
gl_FragColor = newColor;
}
</script>
<!-- main Javascript program -->
<script>
// DECLARE GLOBAL JAVASCRIPT VARIABLES
var canvas = document.getElementById('myCanvas');
var gl; // the WebGL context
var transformationData, transformationAddress;
var x, y, z, angle, scale, time;
function main()
{
// STANDARD STARTUP CODE
gl = setupWebGL(canvas);
var program = initShaders( gl, "vertex-shader", "fragment-shader" );
gl.useProgram( program );
gl.enable(gl.DEPTH_TEST);
// WRITE MAIN JAVASCRIPT CODE HERE
// [x,y,z, r,g,b]
var v0 = [0,0,0]; // back; black
var v1 = [0,0,1]; // z axis; blue
var v2 = [0,1,0]; // y axis; green
var v3 = [0,1,1]; // y+z; cyan
var v4 = [1,0,0]; // x axis; red
var v5 = [1,0,1]; // x+z; magenta
var v6 = [1,1,0]; // x+y; yellow
var v7 = [1,1,1]; // all; white
var uv0 = [0,0];
var uv1 = [0,1];
var uv2 = [1,0];
var uv3 = [1,1];
var cr = [1,0,0]; //red
var cg = [0,1,0]; //green
var cb = [0,0,1]; //blue
var cy = [1,1,0]; //yellow
var co = [1,0.5,0]; //orange
var cw = [1,1,1]; //white
var data = [];
// left x2, right x2, front x2, back x2, up x2, down x2
data = data.concat( v7,uv3,cr,v3,uv1,cr,v1,uv0,cr, v7,uv3,cr,v1,uv0,cr,v5,uv2,cr, // front
v3,uv3,cy,v2,uv1,cy,v0,uv0,cy, v3,uv3,cy,v0,uv0,cy,v1,uv2,cy, // left
v6,uv3,cb,v2,uv1,cb,v3,uv0,cb, v6,uv3,cb,v3,uv0,cb,v7,uv2,cb, // up
v2,uv3,co,v6,uv1,co,v4,uv0,co, v2,uv3,co,v4,uv0,co,v0,uv2,co, // back
v6,uv3,cw,v7,uv1,cw,v5,uv0,cw, v6,uv3,cw,v5,uv0,cw,v4,uv2,cw, // right
v5,uv3,cg,v1,uv1,cg,v0,uv0,cg, v5,uv3,cg,v0,uv0,cg,v4,uv2,cg, v0 ); // down
var attributeArray = new Float32Array( data );
var attributeBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, attributeBuffer );
gl.bufferData( gl.ARRAY_BUFFER, attributeArray, gl.STATIC_DRAW );
var pointPositionAddress =
gl.getAttribLocation( program, "pointPosition" );
var textureCoordinatesAddress =
gl.getAttribLocation( program, "textureCoordinatesA" );
var pointColorAddress =
gl.getAttribLocation( program, "pointColorA" );
var BPE = attributeArray.BYTES_PER_ELEMENT;
gl.vertexAttribPointer(
pointPositionAddress, 3, gl.FLOAT, false, 8*BPE, 0*BPE );
gl.enableVertexAttribArray( pointPositionAddress );
gl.vertexAttribPointer(
textureCoordinatesAddress, 3, gl.FLOAT, false, 8*BPE, 3*BPE );
gl.enableVertexAttribArray( textureCoordinatesAddress );
gl.vertexAttribPointer(
pointColorAddress, 3, gl.FLOAT, false, 8 * BPE, 4*BPE);
gl.enableVertexAttribArray(pointColorAddress);
transformationAddress = gl.getUniformLocation( program, "transformation" );
transformationData = new Matrix4().setIdentity();
x = 0;
y = 0;
z = 0;
angle = 0;
scale = 1;
time = 0;
// set up texture buffer
var textureBuffer = gl.createTexture();
var imageAddress = gl.getUniformLocation( program, "image0" );
var imageData = new Image();
// when image is done loading run some code (load into GPU)
imageData.onload = function() {
loadTexture0(textureBuffer, imageData, imageAddress); }
// start loading the image
imageData.src = "DDBingoGrid.png";
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
loop();
}
// OTHER FUNCTIONS
function loadTexture0( tb, id, ia )
{
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.activeTexture( gl.TEXTURE0 );
gl.bindTexture(gl.TEXTURE_2D, tb);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB,
gl.UNSIGNED_BYTE, id);
gl.uniform1i( ia, 0 );
}
function loop()
{
update();
render();
setTimeout( loop, 16 );
}
// update JavaScript variables; send new data to GPU
function update()
{
time += 0.016; // 60 FPS!
x += 0.00;
y += 0.00;
z += 0.00;
angle += 0.01;
scale += 0.00;
var model = new Matrix4();
var t = new Matrix4();
t.setTranslation(-1/2, 0, -1/2);
var rx = new Matrix4();
rx.setRotationX( angle );
var ry = new Matrix4();
ry.setRotationY(angle);
model = rx.multiply(ry).multiply(t);
var camera = new Matrix4();
camera.setTranslation(0,0,6);
var view = camera.inverse();
var projection = new Matrix4();
projection.setPerspective( 45, 1, 0.1, 100 );
transformationData = projection.multiply(view).multiply(model);
gl.uniformMatrix4fv( transformationAddress, false, transformationData.toArray() );
}
// draw all the things
function render()
{
gl.clear( gl.COLOR_BUFFER_BIT );
gl.clear( gl.DEPTH_BUFFER_BIT );
gl.drawArrays( gl.TRIANGLES, 0, 36 );
}
</script>
</body>
</html>
The problem lies in this line:
newColor = vec4(oldColor.r * pointColorV,
oldColor.g * pointColorV,
oldColor.b * pointColorV,
1.0);
In glsl, the float*vector operation returns a vector. Since pointColorV is a vector, you try to pass three vec4 objects into the vec4 constructor, which is not possible. You can solve that by adding the correct swizzle operator .r/.g/.b after pointColorV. But the better option is to write the whole thing as one operation:
newColor = vec4(oldColor.rgb * pointColorV.rgb, 1.0);
I have been piecing together online examples to make a Mandelbrot Set fragment shader. The vertex shader does basically nothing, it assigns gl_Position and the fragment shader does some math to calculate the image.
However, I have a number of #defines that I want to replace with JavaScript controlled variables and I do not know how to do this. If an example could be shown on how to say replace #define MAX_ITERATIONS 200 with a JavaScript assigned variable in the code below I could probably figure out the rest of them. I believe that I need to specify a uniform or varying but am not sure how to manage the communication from JavaScript to GLSL.
Also I don't understand how aPosition works between JavaScript and the vertex shader, what I have is basically the same as the examples.
JavaScript, I would imagine only init() matters for SO readers, the rest is posted if needed:
var canvas, gl, shaderProgram;
function draw() {
window.requestAnimationFrame(draw, canvas);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function init() {
canvas = document.getElementById("theCanvas");
gl = initGl(canvas);
if (!gl) {
alert("Could not initialize WebGL");
return;
}
shaderProgram = initShaders();
if (!shaderProgram) {
alert("Could not initialize shaders");
return;
}
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0,
]),
gl.STATIC_DRAW
);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
var aPosition = gl.getAttribLocation(shaderProgram, "aPosition");
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
draw();
}
function initGl(inCanvas) {
gl = false;
try { gl = inCanvas.getContext("webgl") || inCanvas.getContext("experimental-webgl"); }
catch (e) {}
return !gl ? false : gl;
}
function initShaders() {
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, document.getElementById("vertexShader").text);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(vertexShader));
return false;
}
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, document.getElementById("fragmentShader").text);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(fragmentShader));
return false;
}
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) return false;
gl.useProgram(shaderProgram);
return shaderProgram;
}
Vertex Shader:
attribute vec2 aPosition;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
}
Fragment Shader, MAX_ITERATIONS, XMIN, YMIN, and WH should be controlled in JavaScript:
#ifdef GL_FRAGEMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
precision mediump int;
#define MAX_ITERATIONS 200
#define XMIN -2.5
#define YMIN -2.0
#define WH 4.0
#define LOG_TWO log(2.0)
#define LOG_MAX log(200.0)
void main() {
// Normalized pixel position to complex plane position
float maxPwh = max(640.0, 480.0);
float x = XMIN+(gl_FragCoord.x/maxPwh)*WH;
float y = YMIN+(gl_FragCoord.y/maxPwh)*WH;
// Complex plane window offsets for pixel windows that are not square
float halfDelta = WH/maxPwh*0.5;
x -= min((640.0-480.0)*halfDelta, 0.0);
y -= min((480.0-640.0)*halfDelta, 0.0);
// Mandelbrot Set code
float zr = x;
float zi = y;
int iterations = 0;
for (int i = 0; i < MAX_ITERATIONS; i++) {
iterations = i;
float sqZr = zr*zr;
float sqZi = zi*zi;
float twoZri = 2.0*zr*zi;
zr = sqZr-sqZi+x;
zi = twoZri+y;
if (sqZr+sqZi > 16.0) break;
}
if (iterations == MAX_ITERATIONS-1) gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
else {
float fn = float(iterations)+1.0-log(log(sqrt(zr*zr+zi*zi)))/LOG_TWO;
float logVal = log(fn)/LOG_MAX;
gl_FragColor = vec4(logVal, logVal, logVal, 1.0);
}
}
The short answer is you have basically 2 options
Pass values from JavaScript to GLSL by uniform.
For example if you want to pass a float create a float uniform
uniform float foo;
In JavaScript compile and link that shader, then lookup the location of the uniform
var locationOfFoo = gl.getUniformLocation(someProgram, "foo");
You can now pass a value to GLSL with
gl.useProgram(someProgram)
gl.uniform1f(locationOfFoo, valueToPass);
Manipulate strings before compiling the shader
#define MAX_INTERATIONS %maxIterations%
#define XMIN %xMin%
...
var maxIterations = 123;
var xMin = 4.5;
shaderSource = shaderSource.replace(/%maxIterations%/g, maxIterations);
shaderSource = shaderSource.replace(/%xMin%/g, xMin);
(1) above is for passing stuff that changes often. #2 is for changing a shader before it's compiled. #1 is a technique used in pretty much 100% of WebGL programs. #2 is used often when generating shaders on the fly which many game engines do.
It took me about 45 minutes to implement gman's answer because
I kept making stupid little mistakes. So here is a full working code
sample that creates an adjustable tile-map.
Tested In: Chrome, Internet Explorer, and Edge:
<!DOCTYPE HTML >
<html lang="en">
<head>
<meta charset="UTF-8">
<title> GL_TILE_TESTBED </title>
<!-- AUTHOR: John Mark Isaac Madison -->
<!-- EMAIL : J4M4I5M7#hotmail.com -->
<!-- SSSSSSSSS SHADER_SECTION START SSSSSSSSS -->
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<style>
p{ font-size:12pt;}
h3,p,input,button,br{
padding:0px;
margin:0px;
font-family:"Andale Mono";
}
button,input{
padding:10px;
}
</style>
<script id="VERT_SHADER" type="NOT_JAVASCRIPT">
precision highp float;
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
</script>
<script id="FRAG_SHADER" type="NOT_JAVASCRIPT">
//Must declare precision before declaring
//any uniforms:
////////////////////////////////////////////////
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
precision mediump int;
////////////////////////////////////////////////
#define CANVAS_WID 640.0
#define CANVAS_HIG 480.0
#define TIL_WID 64.0
#define TIL_HIG 64.0
//Uniforms exposed to HTML/JAVASCRIPT:
uniform float TIL_WID_EDIT;
uniform float TIL_HIG_EDIT;
float til_wid;
float til_hig;
void main() {
//If uniforms have not set by user,
//use the default values set by the #define(s)
//==========================================//
if(TIL_WID_EDIT > 0.0){
til_wid = TIL_WID_EDIT;
}else{
til_wid = TIL_WID;
}
if(TIL_HIG_EDIT > 0.0){
til_hig = TIL_HIG_EDIT;
}else{
til_hig = TIL_HIG;
}
//==========================================//
//NOTE: on "gl_FragCoord" range:
//******************************************//
//web-gl: In terms of pixel/canvas coords.
//OpenGL: In terms of 0 to 1.
//******************************************//
//:Calculate number of tiles shown on screen:
//:This may be fractional:
float NUM_TIL_X = CANVAS_WID / til_wid;
float NUM_TIL_Y = CANVAS_HIG / til_hig;
vec2 FC_MOD;
FC_MOD.x = gl_FragCoord.x;
FC_MOD.y = gl_FragCoord.y;
//You want all tiles to have the full range
//of colors, so you always modulate by
//CANVAS_WID and CANVAS_HIG, You scale by the
//# of tiles on each axis which means the
//gradient becomes steeper as the # of tiles
//increases.
FC_MOD.x = mod( gl_FragCoord.x*NUM_TIL_X, CANVAS_WID );
FC_MOD.y = mod( gl_FragCoord.y*NUM_TIL_Y, CANVAS_HIG );
//[N]ormalize values into range 0 to 1:
//NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN//
float norm_X = (FC_MOD.x) / CANVAS_WID;
float norm_Y = (FC_MOD.y) / CANVAS_HIG;
//NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN//
//Use [B]lue channel because why not?
//BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB//
float GRAD_X = gl_FragCoord.x / CANVAS_WID;
//BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB//
//Set the final [F]ragment colors:
//FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF//
gl_FragColor = vec4(norm_X, norm_Y, GRAD_X, 1.0);
//FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF//
}
</script>
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<!-- SSSSSSSSSS SHADER_SECTION END SSSSSSSSSS -->
</head>
<!-- HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH -->
<!-- BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -->
<body onload="ON_LOADED_FUNCTION()" >
<!-- BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -->
<h3> Open GL Tile TestBed <h3>
<p> Author: John Mark Isaac Madison <p>
<p> Email : J4M4I5M7#hotmail.com <p>
<canvas id="glCanvas"></canvas>
</br>
<button onClick="PUT_WID();">TILE_WIDTH__IN_PIXELS</button>
<input type="text" id="INPUT_WID" value="45">
</br>
</br>
<button onClick="PUT_HIG();">TILE_HEIGHT_IN_PIXELS</button>
<input type="text" id="INPUT_HIG" value="45">
</br>
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<script id="BOILER_PLATE_CODE">
function ON_LOADED_FUNCTION(){
console.log("[ON_LOADED_FUNCTION]");
main();
}
//:Takes the gl context object, if the input
//:is null, we likely failed to get the
//:context.
function HAS_OPEN_GL_CHECK(gl){
// Only continue if WebGL is
// available and working
if (!gl) {
var msg = "";
msg += "[Unable to initialize WebGL.]";
msg += "[your browser or machine may]";
msg += "[not support it.]"
alert( msg );
return;
}
}
function GET_INPUT_BOX_VALUE( elem_id ){
var box; //DOM input box
var val; //Value in input box.
box = document.getElementById( elem_id );
val = box.value;
return (0 + val); //cast to number.
}
function PUT_WID(){
assert_program_and_gl_exist();
var val = GET_INPUT_BOX_VALUE("INPUT_WID");
SET_ATTR("TIL_WID_EDIT", val);
}
function PUT_HIG(){
assert_program_and_gl_exist();
var val = GET_INPUT_BOX_VALUE("INPUT_HIG");
SET_ATTR("TIL_HIG_EDIT", val);
}
function SET_ATTR(gl_var_name, val){
if(val < 0 || val > 256 ){
alert("choose value between 0 to 256");
return;
}
var loc; //<--location of variable.
loc = gl.getUniformLocation(
program ,
gl_var_name
);
gl.useProgram(program);
gl.uniform1f(loc, val);
}
function assert_program_and_gl_exist(){
if(!program){慌("[NO_PROGRAM_EXISTS]");}
if(!gl ){慌("[NO_GL_EXISTS]");}
}
//慌: "disconcerted, be confused, lose one's head"
//慌: In Code: ~Panic~
function 慌( panic_message ){
console.log( panic_message );
alert ( panic_message );
throw ( panic_message );
}
function makeOpenGlContextUsingCanvas(c){
//:Try what works in chrome and all the
//:respectable browsers first:
gl = c.getContext("webgl");
if(!gl){
console.log("[Probably_In_IE]");
gl = c.getContext("experimental-webgl");
}else{
console.log("[Probably_NOT_IE]");
}
HAS_OPEN_GL_CHECK( gl );
return gl;
}
//: No "var" prefix, making them global:
function initGlobals(){
canvas = document.querySelector("#glCanvas");
if(!canvas){
alert("FAILED_TO_GET_CANVAS");
}else{
console.log("[GOT_CANVAS]");
}
gl = makeOpenGlContextUsingCanvas(canvas);
//These dimensions are hard-coded into
//fragment shader code, so be careful
//about changing them:
canvas.width = 640;
canvas.height= 480;
buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
1.0, 1.0]),
gl.STATIC_DRAW
);
//G == Global Container.
//To fix problems with rendering in I.E.
//(Internet Explorer)
//GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG//
var G = {};
G.canvas = canvas;
G.gl = gl;
G.buffer = buffer;
if( ! G.canvas ||
! G.gl ||
! G.buffer ){
慌("[Global_Container_Broken]");
}
return G;
//GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG//
}
function main(){
G = initGlobals();
HAS_OPEN_GL_CHECK( G );
gl.viewport(0,0,gl.drawingBufferWidth, gl.drawingBufferHeight);
setup();
render();
}
function setup(){
var frag_dom = document.getElementById("FRAG_SHADER");
var frag_src = frag_dom.text;
console.log( frag_src );
F = createShader(
gl,gl.FRAGMENT_SHADER, frag_src
);
var vert_dom = document.getElementById("VERT_SHADER");
var vert_src = vert_dom.text;
console.log( vert_src );
V = createShader(
gl, gl.VERTEX_SHADER, vert_src
);
//**** MAKE "program" a GLOBAL VAR ****//
program = createProgram(gl,V,F);
gl.useProgram( program );
if(!program){
慌("PROGRAM_IS_NULL");
}
}
function render(){
window.requestAnimationFrame(render,canvas);
// Set clear color to black, fully opaque
gl.clearColor(0.0, 0.0, 0.5, 1.0);
// Clear the color buffer with specified clear color
gl.clear(gl.COLOR_BUFFER_BIT);
//Directly before call to gl.drawArrays:
positionLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray( positionLocation );
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
function createShader(gl,type,source){
//:Error Check For Bad Inputs:
if(!gl ){慌("[NULL_GL]");}
if(!type ){慌("[NULL_TY]");}
if(!source){慌("[NULL_SR]");}
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
var res = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if( res ){
console.log("[SHADER_COMPILED!]");
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
慌("[FAILED_TO_COMPILE_SHADER]");
}
//:gl : openGL context :
//:vert: vertex shader :
//:frag: fragment shader:
function createProgram(gl,vert, frag){
var program = gl.createProgram();
gl.attachShader(program, vert);
gl.attachShader(program, frag);
gl.linkProgram (program);
var res = gl.getProgramParameter(program, gl.LINK_STATUS);
if( res ){
console.log("[PROGRAM_CREATED!]");
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
</script>
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<!-- BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -->
</body>
</html>