I'm trying to delve into dart's Web GL support, but I can not get it to work. I'm familiar with the "old" version of OpenGL in java (No shaders and such), and I got a pretty good idea of how GL works.
However, I have a problem with my dart WebGL code which I cannot seem to be able to solve.
Here's my code:
import 'dart:html';
import 'dart:web_gl' as WebGL;
import 'dart:typed_data';
import 'dart:math';
import 'package:vector_math/vector_math.dart';
WebGL.RenderingContext gl;
CanvasElement canvas;
Quad quad;
void main() {
canvas = querySelector("#game_canvas");
gl = canvas.getContext("experimental_webgl");
if (gl == null) {
gl = canvas.getContext("webgl");
}
if (gl == null) {
canvas.remove();
querySelector("#error_log").text = "No WebGL";
return;
}
quad = new Quad(new Shader("""
precision highp float;
attribute vec3 a_pos;
uniform mat4 u_transform;
uniform mat4 u_view;
void main() {
gl_Position = u_view * u_transform * vec4(a_pos, 1.0);
}
""", """
precision highp float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
"""
));
gl.enable(WebGL.DEPTH_TEST);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
render();
}
void render() {
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clear(WebGL.COLOR_BUFFER_BIT | WebGL.DEPTH_BUFFER_BIT);
Matrix4 view = makePerspectiveMatrix(70.0*PI/180.0, canvas.width / canvas.height, 0.1, 100.0);
quad.setView(view);
quad.render(new Vector3(150.0, 150.0, 30.0), 100, 100, new Vector4(1.0, 0.0, 0.0, 1.0));
//quad.render(new Vector3(450.0, 150.0, 0.0), 100, 100, new Vector4(0.0, 1.0, 1.0, 1.0));
}
class Quad {
Shader shader;
int posLocation;
WebGL.UniformLocation transformLocation, colorLocation, viewLocation;
Quad(this.shader) {
posLocation = gl.getAttribLocation(shader.program, "a_pos");
transformLocation = gl.getUniformLocation(shader.program, "u_transform");
colorLocation = gl.getUniformLocation(shader.program, "u_color");
viewLocation = gl.getUniformLocation(shader.program, "u_view");
Float32List vertexArray = new Float32List(4 * 3);
vertexArray.setAll(0 * 3, [0.0, 0.0, 0.0]);
vertexArray.setAll(1 * 3, [1.0, 0.0, 0.0]);
vertexArray.setAll(2 * 3, [1.0, 1.0, 0.0]);
vertexArray.setAll(3 * 3, [0.0, 1.0, 0.0]);
Int16List indexArray = new Int16List(6);
indexArray.setAll(0, [0, 1, 2, 0, 2, 3]);
gl.useProgram(shader.program);
gl.enableVertexAttribArray(posLocation);
WebGL.Buffer vertexBuffer = gl.createBuffer();
gl.bindBuffer(WebGL.ARRAY_BUFFER, vertexBuffer);
gl.bufferDataTyped(WebGL.ARRAY_BUFFER, vertexArray, WebGL.STATIC_DRAW);
gl.vertexAttribPointer(posLocation, 3, WebGL.FLOAT, false, 0, 0);
WebGL.Buffer indexBuffer = gl.createBuffer();
gl.bindBuffer(WebGL.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferDataTyped(WebGL.ELEMENT_ARRAY_BUFFER, indexArray, WebGL.STATIC_DRAW);
gl.bindBuffer(WebGL.ELEMENT_ARRAY_BUFFER, indexBuffer);
}
void setView(Matrix4 view) {
gl.uniformMatrix4fv(viewLocation, false, view.storage);
}
Matrix4 matrix = new Matrix4.identity();
void render(Vector3 pos, int w, int h, Vector4 color) {
matrix.setIdentity();
matrix.translate(pos.x - w / 2.0, pos.y - h / 2.0, pos.z);
matrix.scale(w * 1.0, h * 1.0, 0.0);
gl.uniformMatrix4fv(transformLocation, false, matrix.storage);
gl.uniform4fv(colorLocation, color.storage);
gl.drawElements(WebGL.TRIANGLES, 6, WebGL.UNSIGNED_SHORT, 0);
}
}
class Shader {
String vsSource, fsSource;
WebGL.Shader vertexShader, fragmentShader;
WebGL.Program program;
Shader(this.vsSource, this.fsSource) {
compile();
}
void compile() {
vertexShader = gl.createShader(WebGL.VERTEX_SHADER);
gl.shaderSource(vertexShader, vsSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, WebGL.COMPILE_STATUS)) {
throw gl.getShaderInfoLog(vertexShader);
}
fragmentShader = gl.createShader(WebGL.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fsSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, WebGL.COMPILE_STATUS)) {
throw gl.getShaderInfoLog(fragmentShader);
}
program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, WebGL.LINK_STATUS)) {
throw gl.getProgramInfoLog(program);
}
}
}
and my html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="test.css">
</head>
<body>
<p id="error_log"></p>
<div>
<canvas id="game_canvas" width="600" height="300"></canvas>
</div>
<script type="application/dart" src="test.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
All I get is a completely black (color depends on clear color) window. I had similar problems when working in java, and then it was the viewport which was causing it.
However, I'm not sure if that's the problem here.
Replace this line
quad.render(new Vector3(150.0, 150.0, 30.0), 100, 100, new Vector4(1.0, 0.0, 0.0, 1.0));
with
quad.render(new Vector3(0.0, 0.0, -80.0), 100, 100, new Vector4(1.0, 0.0, 0.0, 1.0));
Then you will see the red square.
You created the 1x1 square centered at (0.5,0.5,0.0):
vertexArray.setAll(0 * 3, [0.0, 0.0, 0.0]);
vertexArray.setAll(1 * 3, [1.0, 0.0, 0.0]);
vertexArray.setAll(2 * 3, [1.0, 1.0, 0.0]);
vertexArray.setAll(3 * 3, [0.0, 1.0, 0.0]);
Then you scaled it by (100x, 100x, 0x):
// quad.render(new Vector3(0.0, 0.0, -80.0), 100, 100 ...
// w=100 h=100
matrix.scale(w * 1.0, h * 1.0, 0.0);
Notice the rescaled square center now is (50,50,0).
Then you translated it by (-50,-50,-80):
// quad.render(new Vector3(0.0, 0.0, -80.0), 100, 100 ...
// w=100 h=100 pos=0,0,-80
matrix.translate(pos.x - w / 2.0, pos.y - h / 2.0, pos.z);
So you created a 100x100 square centered at (0,0,-80)
Related
I'm trying to render a lot of points with large gl_PointSize. Looks vertex shader will automatically discard gl.POINTS rendering if the position is out if[-1, 1].
In my case, if the point's position is a little bit out of [-1, 1], part of it still should be shown on the canvas. Any way to let the shader keep rendering point, when position out of [-1, 1]?
Here's the code to draw a point with position a little bit off the canvas. But it is expect to show nearly half of it on canvas.
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
-1.0001, -1
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
....
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);
gl.drawArrays(gl.POINTS, 0, 1);
In my vertex shader, I have
....
gl_PointSize = 32.0;
....
I am unable to repeat your issue. Drawing outside the canvas with POINTS seems to work for me
From the OpenGL ES 2.0 spec section 2.13
If the primitive under consideration is a point, then clipping discards it if it lies outside the near or far clip plane; otherwise it is passed unchanged.
Are you seeing different results?
Of course be aware that both WebGL and OpenGL ES 2.0 are only required to support a max point size of 1.0. It looks like most support at least 60
const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
attribute vec4 position;
void main() {
gl_Position = position;
gl_PointSize = 64.0;
}
`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1.001, -1.001,
1.001, -1.001,
-1.001, 1.001,
1.001, 1.001,
0, 0,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
gl.useProgram(program);
gl.drawArrays(gl.POINTS, 0, 5);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
<canvas></canvas>
Update
According to this thread this is an issue with OpenGL vs OpenGL ES
The OpenGL spec says
If the primitive under consideration is a point, then clipping passes it unchanged if it lies within the clip volume; otherwise, it is discarded.
It's subtly different from the OpenGL ES spec. Effectively OpenGL clips the points, OpenGL ES does not. Even stranger though is that many OpenGL drivers don't clip like the spec claims they are supposed to.
The short version of that means you can't count on whether or not the points are not clipped in WebGL so you might want to consider drawing your own quads instead. You can make your vertex shader expand the quads by whatever value you're currently using for gl_PointSize and either use GPU instancing or manual instancing to draw lots of points. For each POINT position if you're using GPU instancing then there is one position per point just like it is now. If you're using manual instancing then you need to repeat the position for each vertex or add a point Id and put your positions in a texture if you don't want to repeat the positions in the attributes.
Example of using GPU instancing
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
alert('need ANGLE_instanced_arrays');
}
const vs = `
attribute vec4 position; // center point
attribute vec2 cornerPosition; // the corners (-0.5 to 0.5)
uniform vec2 resolution;
varying vec3 pointCoord; // only if you need gl_PointCoord substitute
void main() {
// do the normal thing (can mult by matrix or whatever here
gl_Position = position;
float pointSize = 64.0;
// -- point emulation
gl_Position.xy += cornerPosition * (pointSize * 2.0 - 1.0) /
resolution * gl_Position.w;
// only if you need gl_PointCoord substitute
pointCoord = vec3(cornerPosition * 0.5, gl_Position.z);
}
`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
const programInfo = twgl.createProgram(gl, [vs, fs]);
const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const cornerPositionLoc = gl.getAttribLocation(program, 'cornerPosition');
const resolutionLoc = gl.getUniformLocation(program, 'resolution');
{
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1.001, -1.001,
1.001, -1.001,
-1.001, 1.001,
1.001, 1.001,
0, 0,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(positionLoc, 1);
}
{
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
-0.5, 0.5,
0.5, -0.5,
0.5, 0.5,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);
}
gl.useProgram(program);
gl.uniform2f(resolutionLoc, gl.canvas.width, gl.canvas.height);
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, 5); // 5 points, 6 verts per point
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
You should see this create the same image as above. It has the advantage that it is not subject to the point size limit and works around the driver bugs. Test on your own hardware and see if it solves your issue.
Just remember if you're not using vertex array object then you probably need to reset the attribute divisor back to zero before trying to render something else.
ext.vertexAttribDivisorANGLE(positionLoc, 0);
One more example just to test
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
alert('need ANGLE_instanced_arrays');
}
const vs = `
attribute vec4 position; // center point
attribute vec2 cornerPosition; // the corners (-0.5 to 0.5)
uniform vec2 resolution;
uniform mat4 matrix;
varying vec3 pointCoord; // only if you need gl_PointCoord substitute
void main() {
// do the normal thing (can mult by matrix or whatever here
gl_Position = matrix * position;
float pointSize = 20.0 / gl_Position.w;
// -- point emulation
gl_Position.xy += cornerPosition * (pointSize * 2.0 - 1.0) /
resolution * gl_Position.w;
// only if you need gl_PointCoord substitute
pointCoord = vec3(cornerPosition * 0.5, gl_Position.z);
}
`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
const programInfo = twgl.createProgram(gl, [vs, fs]);
const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const cornerPositionLoc = gl.getAttribLocation(program, 'cornerPosition');
const resolutionLoc = gl.getUniformLocation(program, 'resolution');
const matrixLoc = gl.getUniformLocation(program, 'matrix');
const numPoints = 100;
{
// adapted from http://stackoverflow.com/a/26127012/128511
function fibonacciSphere(samples, i) {
const rnd = 1.;
const offset = 2. / samples;
const increment = Math.PI * (3. - Math.sqrt(5.));
// for i in range(samples):
const y = ((i * offset) - 1.) + (offset / 2.);
const r = Math.sqrt(1. - Math.pow(y ,2.));
const phi = (i + rnd % samples) * increment;
const x = Math.cos(phi) * r;
const z = Math.sin(phi) * r;
return [x, y, z];
}
const positions = [];
for (let i = 0; i < numPoints; ++i) {
positions.push(...fibonacciSphere(numPoints, i));
}
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(positionLoc, 1);
}
{
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
-0.5, 0.5,
0.5, -0.5,
0.5, 0.5,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);
}
function render(ms) {
const secs = ms * 0.001;
const mat = m4.perspective(
60 * Math.PI / 180,
gl.canvas.clientWidth / gl.canvas.clientHeight,
0.1,
100);
m4.translate(mat, [0, 0, -2.11 + Math.sin(secs)], mat);
m4.rotateX(mat, secs, mat);
m4.rotateY(mat, secs * 0.93, mat);
gl.useProgram(program);
gl.uniform2f(resolutionLoc, gl.canvas.width, gl.canvas.height);
gl.uniformMatrix4fv(matrixLoc, false, mat);
// 6 verts per point
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, numPoints);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
I'm working on learning WebGL and am confused by a very elementary problem to do with model-view and projection matrices.
In the snippet below, if I use the following line to set the point positions, the points render:
gl_Position = vec4(aVertexPosition, 1.0);
However, if I try to use the projection matrix and the model view matrix, the points disappear:
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
If uPMatrix and uMVMatrix are identity matrices (as I thought they should be, as I'm not intentionally manipulating them), then the gl_Position value should be set and the points should be rendered (the following works):
gl_Position = mat4(1.0) * mat4(1.0) * vec4(aVertexPosition, 1.0);
However, logging the uPMatrix and uMVMatrix values in the render() loop, I can see that the projection matrix (uPMatrix) values are not an identity matrix! Does anyone know why this is the case? Full snippet below:
var canvas,
gl,
fs,
vs,
glProgram,
vertexBuffer,
vertexIndexBuffer,
colorBuffer,
positionVal,
colorVal,
points = [],
mvMatrix = mat4.create(),
pMatrix = mat4.create(),
angle = 0.00;
function initWebgl() {
canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
try {
gl = canvas.getContext('webgl')
} catch(err) {
alert('Your browser does not support Webgl')
}
// set the default background color
gl.clearColor(0.9, 0.9, 0.9, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);
}
function initCamera() {
// set camera area, fov, near clip, far clip, and translation
gl.viewport(0, 0, canvas.width, canvas.height)
mat4.perspective(45, canvas.width/canvas.height, 0.1, 100.0, pMatrix);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [0, 0, 0]);
}
function initShaders() {
vs = buildShader('#shader-vs', gl.VERTEX_SHADER)
fs = buildShader('#shader-fs', gl.FRAGMENT_SHADER)
}
function buildShader(selector, type) {
var src = document.querySelector(selector).innerHTML;
var shader = gl.createShader(type)
gl.shaderSource(shader, src)
gl.compileShader(shader)
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.warn('Shader error', selector, gl.getShaderInfoLog(shader))
}
return shader;
}
function initProgram() {
glProgram = gl.createProgram()
gl.attachShader(glProgram, vs)
gl.attachShader(glProgram, fs)
gl.linkProgram(glProgram)
if (!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) {
console.warn('Program link error')
}
gl.useProgram(glProgram)
}
function updatePositions() {
mat4.identity(mvMatrix)
//mat4.translate(mvMatrix, [0.0, 0.0, 0.0])
//mat4.rotate(mvMatrix, angle, [0.0, 0.0, 0.0])
angle += 0.01;
}
function getBuffers() {
// vertex buffer
points = [
-0.5, 0.5, 0.0,
0.0, 0.0, 0.0,
-0.5, -0.5, 0.0,
]
var vertexData = new Float32Array(points);
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW)
}
function drawBuffers() {
// identify and bind vertex position attributes
var aVertexPosition = gl.getAttribLocation(glProgram, 'aVertexPosition')
gl.enableVertexAttribArray(aVertexPosition)
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0.0, 0.0)
// draw the data
gl.drawArrays(gl.POINTS, 0, 3)
}
function getMatrixUniforms() {
glProgram.pMatrixUniform = gl.getUniformLocation(glProgram, 'uPMatrix')
glProgram.mvMatrixUniform = gl.getUniformLocation(glProgram, 'uMVMatrix')
}
function setMatrixUniforms() {
gl.uniformMatrix4fv(glProgram.pMatrixUniform, false, pMatrix)
gl.uniformMatrix4fv(glProgram.mvMatrixUniform, false, mvMatrix)
}
function render() {
updatePositions()
drawBuffers()
setMatrixUniforms()
requestAnimationFrame(render, canvas)
}
initWebgl()
initCamera()
initShaders()
initProgram()
getMatrixUniforms()
getBuffers()
render()
* {
margin: 0;
padding: 0;
}
body, html {
height: 100%;
width: 100%;
overflow: hidden;
background: skyblue;
}
<script src="https://rawgit.com/duhaime/955402641534b89babd41c8de8bc91f6/raw/5d86d54f7237f4cf2b206dcf0a3d453ba95acd1d/gl-matrix.js"></script>
<script id='shader-vs' type='x-shader/x-vertex'>
attribute vec3 aVertexPosition;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying highp vec3 vPos;
void main() {
gl_PointSize = 5.0;
// this works
gl_Position = vec4(aVertexPosition, 1.0);
// this makes all points disappear
//gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vPos = vec3(aVertexPosition);
}
</script>
<script id='shader-fs' type='x-shader/x-fragment'>
varying highp vec3 vPos;
void main() {
highp vec3 blue = vec3(1.0, 1.0, 1.0);
highp vec3 red = vec3(1.0, 0.0, 0.0);
gl_FragColor = vec4(mix(blue, red, 1.0), 1.0);
}
</script>
<canvas />
The gl-matrix library initializes matrices with zeros, and I was only transforming the model-view matrix, not the projection matrix, to an identity matrix. I should have been initializing the projection matrix as an identity matrix:
function initCamera() {
// set camera area, fov, near clip, far clip, and translation
gl.viewport(0, 0, canvas.width, canvas.height)
mat4.perspective(45, canvas.width/canvas.height, 0.1, 100.0, pMatrix);
mat4.identity(mvMatrix);
mat4.identity(pMatrix);
mat4.translate(mvMatrix, [0, 0, 0]);
}
[Is it customary for users to initialize their mv and p matrices to identity matrices?]
I made a triangle list with 4 triangles, having the middle point a different color. And then aim to combine the triangles to get a nice gradient.
But the edges of the triangles create unwanted lines, I don't want these lines I want it to be smooth al the way.
How can I get the desired result?
Images:
Shader Code:
// Simple passthrough vertex shader
//
attribute vec3 in_Position; // (x,y,z)
attribute vec4 in_Colour; // (r,g,b,a)
attribute vec2 in_TextureCoord; // (u,v)
varying vec2 v_texcoord;
varying vec4 v_colour;
void main()
{
vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
v_colour = in_Colour;
v_texcoord = in_TextureCoord;
}
//
// Simple passthrough fragment shader
//
varying vec2 v_texcoord;
varying vec4 v_colour;
void main()
{
gl_FragColor = v_colour;
}
Gamemaker code:
Create event:
//Build vertices list
vertex_format_begin();
vertex_format_add_position();
vertex_format_add_colour();
vertex_format_add_textcoord();
v_format = vertex_format_end();
v_buff = vertex_create_buffer();
vertex_begin(v_buff, v_format);
//triangle 0
vertex_position(v_buff, 200, 100);
vertex_colour(v_buff, c_black, 1);
vertex_texcoord(v_buff, 0.0, 0.0);
vertex_position(v_buff, 600, 100);
vertex_colour(v_buff, c_black, 1);
vertex_texcoord(v_buff, 1.0, 0.0);
vertex_position(v_buff, 400, 300);
vertex_colour(v_buff, c_red, 1);
vertex_texcoord(v_buff, 0.5, 0.5);
//triangle 1
vertex_position(v_buff, 200, 100);
vertex_colour(v_buff, c_black, 1);
vertex_texcoord(v_buff, 0.0, 0.0);
vertex_position(v_buff, 200, 500);
vertex_colour(v_buff, c_black, 1);
vertex_texcoord(v_buff, 0.0, 1.0);
vertex_position(v_buff, 400, 300);
vertex_colour(v_buff, c_red, 1);
vertex_texcoord(v_buff, 0.5, 0.5);
//triangle 2
vertex_position(v_buff, 600, 100);
vertex_colour(v_buff, c_black, 1);
vertex_texcoord(v_buff, 1.0, 0.0);
vertex_position(v_buff, 600, 500);
vertex_colour(v_buff, c_black, 1);
vertex_texcoord(v_buff, 1.0, 1.0);
vertex_position(v_buff, 400, 300);
vertex_colour(v_buff, c_red, 1);
vertex_texcoord(v_buff, 0.5, 0.5);
//triangle 3
vertex_position(v_buff, 200, 500);
vertex_colour(v_buff, c_black, 1);
vertex_texcoord(v_buff, 0.0, 1.0);
vertex_position(v_buff, 600, 500);
vertex_colour(v_buff, c_black, 1);
vertex_texcoord(v_buff, 1.0, 1.0);
vertex_position(v_buff, 400, 300);
vertex_colour(v_buff, c_red, 1);
vertex_texcoord(v_buff, 0.5, 0.5);
vertex_end(v_buff);
tex = sprite_get_texture(sprite_index, 0);
Draw event:
shader_set(shd_prim);
shader_set_uniform_f(uni_radius, var_radius);
vertex_submit(v_buff, pr_trianglelist, tex);
shader_reset();
The effect that you can see is optical illusion. You can make this visible by grading the colors. Use the following fragment shader for this:
varying vec2 v_texcoord;
varying vec4 v_colour;
void main()
{
float steps = 4.0;
//float steps = 8.0;
//float steps = 16.0;
//float steps = 32.0;
vec3 gradColor = floor(v_colour.rgb * steps) / steps;
gl_FragColor = vec4(gradColor, 1.0);
}
4 colors:
8 colors:
16 colors:
32 colors:
To achieve a better result, you have to do the color calculated in the fragment shader. The following shader smoothly change the gradient, from a circular gradient in the middle of the the view, to a square gradient at the borders of the view. The fragment color is interpolated form color1 to color2, using the GLSL mix function.
varying vec2 v_texcoord;
varying vec4 v_colour;
void main()
{
vec4 color1 = vec4(1.0, 0.0, 0.0, 1.0);
vec4 color2 = vec4(0.0, 0.0, 0.0, 1.0);
vec2 distV = v_texcoord * 2.0 - 1.0;
float maxDist = max(abs(distV.x), abs(distV.y));
float circular = length(distV);
float square = maxDist;
gl_FragColor = mix(color1, color2, mix(circular,square,maxDist));
}
Preview:
I'm very new to shaders and have modified a shader on glsl.heroku.com to suit my needs, see it here - this is what I want my 3D Object to look like: Demo. Now my question is how do I import it into my project using native webgl (View my broken code here). The shader code is as follows:
precision mediump float;
varying vec2 surfacePosition;
void main( void ) {
float intensity = 3.0; // Lower number = more 'glow'
vec3 light_color = vec3(0.4, 0.3, 0.1); // RGB, proportional values, higher increases intensity
float master_scale = 0.01; // Change the size of the effect
float c = master_scale/(length(surfacePosition) * length(surfacePosition));
gl_FragColor = vec4(vec3(pow(c, intensity))*light_color, 1.0);
}
That's the fragment shader, so how do I build the correct vertex shader to go with it and pass through the surfacePosition variable (and what is the surface position variable???) using my broken code in the link above.
Without looking at the source I'm going to guess surfacePosition is just the vertex position * some matrix.
Vertex shader
attribute vec4 a_position;
varying vec2 surfacePosition;
uniform mat4 mat;
void main() {
gl_Position = a_position;
surfacePosition = (a_position * mat).xy;
}
setup
var verts = [
1, 1,
-1, 1,
-1, -1,
1, 1,
-1, -1,
1, -1,
];
var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
...
var loc = gl.getUniformLocation(program, "mat");
var mat = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
];
mat[5] = canvas.height/canvas.width;
gl.uniformMatrix4fv(loc, false, mat);
gl.drawArrays(gl.TRIANGLES, 0, 6);
The matrix lets you adjust the rendering aspect based on the size of the canvas as well as implement pan and zoom. See this article.
here's a snippet
var countElem = document.getElementById("t");
var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
var program = twgl.createProgramFromScripts(
gl, ["vshader", "fshader"], ["a_position"]);
gl.useProgram(program);
var verts = [
1, 1,
-1, 1,
-1, -1,
1, 1,
-1, -1,
1, -1,
];
var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
var loc = gl.getUniformLocation(program, "mat");
var mat = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
];
// index 0 = x scale (ie, aspect & zoom)
// index 5 = y scale (ie, aspect & zoom)
// index 12 = x offset (pan x)
// index 13 = y offset (pan y)
mat[5] = canvas.height/canvas.width;
gl.uniformMatrix4fv(loc, false, mat);
gl.drawArrays(gl.TRIANGLES, 0, 6);
<script src="https://twgljs.org/dist/3.x/twgl.min.js"></script>
<script id="vshader" type="whatever">
attribute vec4 a_position;
varying vec2 surfacePosition;
uniform mat4 mat;
void main() {
gl_Position = a_position;
surfacePosition = (a_position * mat).xy;
}
</script>
<script id="fshader" type="whatever">
precision mediump float;
varying vec2 surfacePosition;
void main() {
float intensity = 3.0; // Lower number = more 'glow'
vec3 light_color = vec3(0.4, 0.3, 0.1); // RGB, proportional values, higher increases intensity
float master_scale = 0.01; // Change the size of the effect
float c = master_scale/(length(surfacePosition) * length(surfacePosition));
gl_FragColor = vec4(vec3(pow(c, intensity))*light_color, 1.0);
}
</script>
<canvas id="c"></canvas>
In OpenGL you can draw define points like this:
glBegin(GL_POINTS);
for(float theta=0, radius=60.0; radius>1.0; theta+=0.1, radius-=0.3){
glColor3f(radius/60.0,0.3,1-(radius/60.0));
glVertex2i(200+radius*cos(theta),200+radius*sin(theta));
}
glEnd();
How do you accomplish this same functionality in WebGL?
The code you wrote really doesn't do much except define some points. To do that in WebGL could do it like this
var colors = [];
var verts = [];
var theta=0
for(var radius=60.0; radius>1.0; radius-=0.3) {
colors.push(radius/60.0, 0.3, 1-(radius/60.0));
verts.push(200+radius*Math.cos(theta),200+radius*Math.sin(theta));
theta+=0.1;
}
var numPoints = colors.length / 3;
That would make 2 JavaScript arrays. You'd then need to put them to WebGLBuffers
var colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
After that though you need to write a shader and set it up. Shaders are a huge topic. For your particular data I'm guessing these shader would do
A vertex shader
uniform mat4 u_matrix;
attribute vec4 a_vertex;
attribute vec4 a_color;
varying vec4 v_color;
void main() {
// Set the size of the point
gl_PointSize = 3.0;
// multiply each vertex by a matrix.
gl_Position = u_matrix * a_vertex;
// pass the color to the fragment shader
v_color = a_color;
}
A fragment shader
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
Next you need to initialize the shaders and parameters. I'm going to assume I put the shaders in script tags with ids "vshader" and "fshader" and use this boilerplate code to load them.
var program = createProgramFromScriptTags(gl, "vshader", "fshader");
gl.useProgram(program);
// look up the locations for the inputs to our shaders.
var u_matLoc = gl.getUniformLocation(program, "u_matrix");
var colorLoc = gl.getAttribLocation(program, "a_color");
var vertLoc = gl.getAttribLocation(program, "a_vertex");
// Set the matrix to some that makes 1 unit 1 pixel.
gl.uniformMatrix4fv(u_matLoc, false, [
2 / width, 0, 0, 0,
0, 2 / height, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]);
// Tell the shader how to get data out of the buffers.
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.vertexAttribPointer(colorLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(colorLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.vertexAttribPointer(vertLoc, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vertLoc);
and finally draw the points
gl.drawArrays(gl.POINTS, 0, numPoints);
Here's a snippet
var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
if (!gl) {
alert("no WebGL");
//return;
}
var colors = [];
var verts = [];
var theta=0
for(var radius=160.0; radius>1.0; radius-=0.3) {
colors.push(radius/160.0, 0.3, 1-(radius/160.0));
verts.push(radius*Math.cos(theta),radius*Math.sin(theta));
theta+=0.1;
}
var numPoints = colors.length / 3;
var colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
var program = twgl.createProgramFromScripts(gl, ["vshader", "fshader"]);
gl.useProgram(program);
// look up the locations for the inputs to our shaders.
var u_matLoc = gl.getUniformLocation(program, "u_matrix");
var colorLoc = gl.getAttribLocation(program, "a_color");
var vertLoc = gl.getAttribLocation(program, "a_vertex");
function draw() {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
// Set the matrix to some that makes 1 unit 1 pixel.
gl.uniformMatrix4fv(u_matLoc, false, [
2 / canvas.width, 0, 0, 0,
0, -2 / canvas.height, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]);
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.vertexAttribPointer(colorLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(colorLoc);
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.vertexAttribPointer(vertLoc, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vertLoc);
gl.drawArrays(gl.POINTS, 0, numPoints);
requestAnimationFrame(draw, canvas);
}
draw();
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/3.x/twgl.min.js"></script>
<script id="vshader" type="whatever">
uniform mat4 u_matrix;
attribute vec4 a_vertex;
attribute vec4 a_color;
varying vec4 v_color;
void main() {
// Set the size of the point
gl_PointSize = length(a_vertex) * 0.1;
// multiply each vertex by a matrix.
gl_Position = u_matrix * a_vertex;
// pass the color to the fragment shader
v_color = a_color;
}
</script>
<script id="fshader" type="whatever">
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
</script>
<canvas id="c" width="400" height="400"></canvas>
you might find these WebGL tutorials helpful.
WebGL is based on OpenGL ES 2.0 (see here), which dropped immediate-mode support.
This specification describes an additional rendering context and support objects for the HTML 5 canvas element [CANVAS]. This context allows rendering using an API that conforms closely to the OpenGL ES 2.0 API.
You'll need to use vertex buffers to store vertex data. See here1 for a good explanation of how things work in retained mode. And there for a nice small example to get you started.
1: Kudos to whoever posted this here.