Smooth / blur WebGL Polygons - javascript

I have some code that generates two WebGL triangles, one green and one blue, and puts them on a mapbox-gl-js map. The four coordinates are those of four European cities. Here is the code I have:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Add a custom style layer</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v2.12.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.12.0/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1Ijoic3RlZXBhdHRpY3N0YWlycyIsImEiOiJjbDNvaGFod2EwbXluM2pwZTJiMDYzYjh5In0.J_HeH00ry0tbLmGmTy4z5w';
const map = new mapboxgl.Map({
container: 'map',
zoom: 3,
center: [7.5, 58],
// Choose from Mapbox's core styles, or make your own style with Mapbox Studio
style: 'mapbox://styles/mapbox/light-v11',
antialias: true, // create the gl context with MSAA antialiasing, so custom layers are antialiased
projection: 'mercator'
});
// define vertices of the triangle to be rendered in the custom style layer
const helsinki = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 25.004,
lat: 60.239
});
const berlin = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 13.403,
lat: 52.562
});
const kyiv = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 30.498,
lat: 50.541
});
const moscow = mapboxgl.MercatorCoordinate.fromLngLat({
lng: 37.6173,
lat: 55.7558
});
const vertex_buffer = new Float32Array([
helsinki.x,
helsinki.y,
berlin.x,
berlin.y,
kyiv.x,
kyiv.y,
kyiv.x,
kyiv.y,
helsinki.x,
helsinki.y,
moscow.x,
moscow.y
])
const color_buffer = new Float32Array([
0.0, 1.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 0.0, 1.0, 1.0,
0.0, 0.0, 1.0, 1.0,
])
const highlightLayer = {
id: 'highlight',
type: 'custom',
// method called when the layer is added to the map
// https://docs.mapbox.com/mapbox-gl-js/api/#styleimageinterface#onadd
onAdd: function (map, gl) {
// create GLSL source for vertex shader
const vertexSource = `
uniform mat4 u_matrix;
attribute vec2 a_pos;
attribute vec4 color;
varying vec4 vColor;
void main() {
gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);
vColor = color;
}`;
// create GLSL source for fragment shader
const fragmentSource = `
precision lowp float;
varying vec4 vColor;
void main() {
gl_FragColor = vec4(vColor);
}`;
// create a vertex shader
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
// create a fragment shader
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
// link the two shaders into a WebGL program
this.program = gl.createProgram();
gl.attachShader(this.program, vertexShader);
gl.attachShader(this.program, fragmentShader);
gl.linkProgram(this.program);
this.aPos = gl.getAttribLocation(this.program, 'a_pos');
this.color = gl.getAttribLocation(this.program, 'color');
// create and initialize a WebGLBuffer to store vertex and color data
this.vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
vertex_buffer,
gl.STATIC_DRAW
);
this.colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
color_buffer,
gl.STATIC_DRAW
);
},
// method fired on each animation frame
// https://docs.mapbox.com/mapbox-gl-js/api/#map.event:render
render: function (gl, matrix) {
gl.useProgram(this.program);
gl.uniformMatrix4fv(
gl.getUniformLocation(this.program, 'u_matrix'),
false,
matrix
);
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
gl.enableVertexAttribArray(this.aPos);
gl.vertexAttribPointer(this.aPos, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer);
gl.enableVertexAttribArray(this.color);
gl.vertexAttribPointer(this.color, 4, gl.FLOAT, false, 0, 0);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArrays(gl.TRIANGLES, 0, vertex_buffer.length / 2);
}
};
// add the custom style layer to the map
map.on('load', () => {
map.addLayer(highlightLayer, 'building');
});
</script>
</body>
</html>
This generates an image that looks like this:
However, I want to blur, or smooth the polygons, so they look like this:
(disregard the fact that the map is also blurred, I just blurred the whole image so you can understand what I want the polygons to look like)
Is there any way I can achieve this? Maybe I can implement a Gaussian blur, or use some type of shader? I'm rather new to WebGL, so I would really appreciate some help.

Related

With Instancing, I am not able to change the transparency/opacity of the individual child geometries

I have a simple model consisting of 1000 instances of sphere. I am trying to use instancing to reduce the number of draw calls. However, I am not able to change the transparency/opacity of the individual child geometries.
I already tried the following things:
I am able to change transparency of every sphere using
material.fragmentShader = "varying vec3 vColor;void main() { gl_FragColor = vec4( vColor, 0.2 );}";
However, that changes the opacity to 0.2 for every sphere.
The html file is like:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>A-Frame Instancing component</title>
<meta name="description" content="A-Frame Instancing component">
<script>
/*
var WebVRConfig = {
FORCE_ENABLE_VR: true,
BUFFER_SCALE: 1.0
};
*/
</script>
<script src="https://cdn.rawgit.com/aframevr/aframe/v0.4.0/dist/aframe-master.min.js"></script>
<script src="https://cdn.rawgit.com/donmccurdy/aframe-extras/v3.2.0/dist/aframe-extras.min.js"></script>
<script type="text/javascript" src="build/aframe-instancing.js"></script>
</head>
<body>
<a-scene stats>
<a-assets>
<img id="sky" src="https://cdn.rawgit.com/aframevr/aframe/master/examples/primitives/models/peach-gradient.jpg">
</a-assets>
<a-entity instancing="count:100"></a-entity>
<a-sky src="#sky"></a-sky>
<a-entity light="type:directional;color:#FFFFFF" position="-1 1 1"></a-entity>
</a-scene>
</body>
</html>
The function to acheive instancing:
AFRAME.registerComponent('instancing', {
schema: {
count: {type: 'int', default: 10000}
},
var geometry = new THREE.InstancedBufferGeometry();
geometry.copy(new THREE.SphereBufferGeometry(5.0));
var translateArray = new Float32Array(count*3);
var vectorArray = new Float32Array(count*3);
var colorArray = new Float32Array(count*3);
geometry.addAttribute('translate', new THREE.InstancedBufferAttribute(translateArray, 3, 1));
geometry.addAttribute('vector', new THREE.InstancedBufferAttribute(vectorArray, 3, 1));
geometry.addAttribute('color', new THREE.InstancedBufferAttribute(colorArray, 3, 1));
var material = new THREE.ShaderMaterial({
uniforms: {
time: {value: 0}
},
vertexShader: [
'attribute vec3 translate;',
'attribute vec3 vector;',
'attribute vec3 color;',
'uniform float time;',
'varying vec3 vColor;',
'const float g = 9.8 * 1.5;',
'void main() {',
' vec3 offset;',
' offset.xz = vector.xz * time;',
' offset.y = vector.y * time - 0.5 * g * time * time;',
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position + translate + offset, 1.0 );',
' vColor = color;',
'}'
].join('\n'),
fragmentShader: [
'varying vec3 vColor;',
'void main() {',
' gl_FragColor = vec4( vColor, 1 );',
'}'
].join('\n')
});
var mesh = new THREE.Mesh(geometry, material);
this.model = mesh;
el.setObject3D('mesh', mesh);
el.emit('model-loaded', {format:'mesh', model: mesh});
//try to change opacity here
material.fragmentShader = "varying vec3 vColor;void main() { gl_FragColor = vec4( vColor, 0.2 );}";
material.transparent = true;
//use the new opacity
var mesh1 = new THREE.Mesh(geometry1, material);
this.mesh = mesh1;
el.setObject3D('mesh', mesh1);
el.emit('model-loaded', {format:'mesh', model: mesh1});
}
});
}
]);
Can anyone please tell me, how to change opacity of only one sphere? Thank you in advance!
Also, suppose I am trying to replicate multiple boxes.
One of which is as following:
<a-box position="19.0 1.5 23.0"
width="32.0"
height="1.0"
depth="40.0"
color="#969696"
shader="flat"
flat-shading="true">
</a-box>
What would be the values I would fill in translateArray & vectorArray ?
Thanks a lot in advance!
Your colors are only RGB values, not RGBA. Update your color attribute to support 4 values, and change associated vec3 references to use vec4. The last value in the vector will be your alpha (transparency) value.
It looks like you already know how to send the value from your vertex shader into your fragment shader, so I won't go into detail there. But in your fragment shader, you can use the color directly, because gl_FragColor expects to be set to a vec4.
Further clarification:
When you create a InstancedBufferAttribute, you create one attribute per instance. So your color attribute currently only carries RGB values for each instance.
Hard-coding 1 or 0.2 in the w place (i.e. gl_FragColor = vec4( vColor, 1 );), you apply it universally to all instances. So, you need to define the alpha value on a per-instance basis, and the easiest way to do this is through your already-created and instanced color attribute.
//geometry.addAttribute('color', new THREE.InstancedBufferAttribute(colorArray, 3, 1)
geometry.addAttribute('color', new THREE.InstancedBufferAttribute(colorArray, 4, 1)
The code above makes room for the alpha values, which you should supply for each sphere. Your colorArray will contain data like [ R, G, B, A, R, G, B, A, ... ].
Then, in your shaders...
// vertex shader
// ...
attribute vec4 color;
varying vec4 vColor;
// ...
void main(){
// ...
vColor = color;
// ...
}
// fragment shader
// ...
varying vec4 vColor;
// ...
void main(){
// ...
gl_FragColor = vColor;
}
Now, the alpha value that you supplied for each sphere instance will be used only for that instance. For example, if you want the sphere at index 1 to be the only transparent sphere, your colorArray buffer should look like this:
colorArray = [
1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 0.5,
1.0, 1.0, 1.0, 1.0,
// ...
];`
Important caveat
This implementation does not depth-sort the instances, and so blending will be dependent on render order. You can read more about this in the following question:
Transparency within instanced shapes

WebGL: Set opacity when using texture2D()

I have a simple WebGL scene created by Three.js for which I'm using a custom shader. The scene contains an image and I'd like to set the opacity of the image, but manipulating the .a attribute of gl_FragColor is not doing the trick.
Does anyone know how to set the opacity of an image in WebGL? I'd be grateful for any ideas others can offer on this question!
<html>
<head>
<style>
html, body { width: 100%; height: 100%; background: #000; }
body { margin: 0; overflow: hidden; }
canvas { width: 100%; height: 100%; }
</style>
</head>
<body>
<script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js'></script>
<script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script>
<script type='x-shader/x-vertex' id='vertex-shader'>
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 cameraPosition;
attribute vec2 uv;
attribute vec3 position;
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float; // set float precision (optional)
uniform sampler2D texture; // identify the texture as a uniform argument
varying vec2 vUv; // identify the uv values as a varying attribute
void main() {
vec4 color = texture2D(texture, vUv);
gl_FragColor = vec4(color[0], color[1], color[2], 0.0);
}
</script>
<script>
function getScene() {
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
return scene;
}
function getCamera() {
var aspectRatio = window.innerWidth / window.innerHeight;
var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 1000);
camera.position.set(0, 1, 10);
return camera;
}
function getRenderer() {
// Create the canvas with a renderer
var renderer = new THREE.WebGLRenderer({antialias: true});
// Add support for retina displays
renderer.setPixelRatio(window.devicePixelRatio);
// Specify the size of the canvas
renderer.setSize(window.innerWidth, window.innerHeight);
// Add the canvas to the DOM
document.body.appendChild(renderer.domElement);
return renderer;
}
function getControls(camera, renderer) {
var controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.zoomSpeed = 0.4;
controls.panSpeed = 0.4;
return controls;
}
function loadImage() {
var geometry = new THREE.BufferGeometry();
/*
Now we need to push some vertices into that geometry to identify the coordinates the geometry should cover
*/
// Identify the image size
var imageSize = {width: 10, height: 7.5};
// Identify the x, y, z coords where the image should be placed
var coords = {x: -5, y: -3.75, z: 0};
// Add one vertex for each corner of the image, using the
// following order: lower left, lower right, upper right, upper left
var vertices = new Float32Array([
coords.x, coords.y, coords.z, // bottom left
coords.x+imageSize.width, coords.y, coords.z, // bottom right
coords.x+imageSize.width, coords.y+imageSize.height, coords.z, // upper right
coords.x, coords.y+imageSize.height, coords.z, // upper left
])
// set the uvs for this box; these identify the following corners:
// lower-left, lower-right, upper-right, upper-left
var uvs = new Float32Array([
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
])
// indices = sequence of index positions in `vertices` to use as vertices
// we make two triangles but only use 4 distinct vertices in the object
// the second argument to THREE.BufferAttribute is the number of elements
// in the first argument per vertex
geometry.setIndex([0,1,2, 2,3,0])
geometry.addAttribute('position', new THREE.BufferAttribute( vertices, 3 ));
geometry.addAttribute('uv', new THREE.BufferAttribute( uvs, 2) )
// Create a texture loader so we can load our image file
var loader = new THREE.TextureLoader();
// specify the url to the texture
var url = 'https://s3.amazonaws.com/duhaime/blog/tsne-webgl/assets/cat.jpg';
// specify custom uniforms and attributes for shaders
// Uniform types: https://github.com/mrdoob/three.js/wiki/Uniforms-types
var material = new THREE.RawShaderMaterial({
uniforms: {
texture: {
type: 't',
value: loader.load(url)
},
transparent: true,
},
vertexShader: document.getElementById('vertex-shader').textContent,
fragmentShader: document.getElementById('fragment-shader').textContent
});
// Combine our image geometry and material into a mesh
var mesh = new THREE.Mesh(geometry, material);
// Set the position of the image mesh in the x,y,z dimensions
mesh.position.set(0,0,0)
// Add the image to the scene
scene.add(mesh);
}
/**
* Render!
**/
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
controls.update();
};
var scene = getScene();
var camera = getCamera();
var renderer = getRenderer();
var controls = getControls(camera, renderer);
loadImage();
render();
</script>
</body>
</html>
You need to set
material.transparent = true;
Doing so will force normal alpha-blending to be set automatically by the renderer.
three.js r.95

Webgl flickering in Chrome on Windows x64 with nvidia GPU

I see a weird flickering of some rendered geometry Chrome on Windows 10 x64 with nVidia chips. I've also tested in in Chrome for Linux, Firefox for both platforms, Android, and with Intel GPU. It works fine everywhere, except the one platform mentioned.
Minimal example looks like this:
Vertex shader:
precision mediump float;
smooth out vec2 pointCoord;
const vec2 vertexCoord[] = vec2[](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(1.0, 1.0),
vec2(0.0, 0.0),
vec2(1.0, 1.0),
vec2(0.0, 1.0)
);
void main()
{
gl_Position = vec4(vertexCoord[gl_VertexID], 0.0, 1.0);
pointCoord = vertexCoord[gl_VertexID];
}
Fragment shader:
precision mediump float;
out vec4 outFragColor;
smooth in vec2 pointCoord;
void main()
{
outFragColor = vec4(pointCoord, 0.0, 1.0);
}
GL state:
gl.clearColor(0, 0, 0, 1);
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
gl.depthMask(gl.FALSE);
gl.stencilMask(gl.FALSE);
Rendering code:
var mainLoop = function()
{
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6);
window.requestAnimationFrame(mainLoop);
}
window.requestAnimationFrame(mainLoop);
Working web page with full code is available here.
What I see on affected platforms:
I tried webgl1 context with a vertex attribute, as there is no gl_VertexID variable in glsl 100es, and it produces the same result. I also tried to add glFinish or glFlush at the end of main loop. It reduces flicker frequency, but does not solve the problem.
What is the problem? Is there any undefined behavior in this code, so it works different ways on different platforms? Or is it a bug in Chrome or Angle?
UPD:
Following Nabr's answer, I added a VBO bound to enabled vertex attribute, and constructed another example which flickers on my machines.
UPD2:
Reported a bug to Chromium project:
https://bugs.chromium.org/p/chromium/issues/detail?id=836788
no, flicker here. The quad isn't showing at all.
Chrome 65
Windows 10
NVidia GTX 1050
i follow this since months, the implementation from browser (release) to browser and it's platform is different. you can't have a bufferless "shape" crossplatform, my experience.
you on the save side if you bindBuffer, see here:
https://stackoverflow.com/a/44448514
// added a buffer array
// note: commented some consolelogs out, it looks nicer on stackoverflow
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl2");
if (!gl) {
console.log("Could not get context.");
throw new Error();
}
var checkError = function() {
if (gl.getError() != gl.NO_ERROR) {
console.log("Webgl error.");
throw new Error();
}
}
// GL setup
gl.clearColor(0, 0, 0, 1);
gl.disable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
gl.depthMask(gl.FALSE);
gl.stencilMask(gl.FALSE);
// Shader
var vertexSource = document.getElementById("vertexShader").text;
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexSource);
gl.compileShader(vertexShader);
// console.log("Vertex log: " + gl.getShaderInfoLog(vertexShader));
var fragmentSource = document.getElementById("fragmentShader").text;
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentSource);
gl.compileShader(fragmentShader);
// console.log("Fragment log: " + gl.getShaderInfoLog(fragmentShader));
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
// console.log("Link log: " + gl.getProgramInfoLog(program));
checkError();
gl.useProgram(program);
var time_loc = gl.getUniformLocation(program, "time");
// CHANGED
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 0.0,
1.0, 1.0,
0.0, 1.0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.bindVertexArray(null);
var tick = 0;
var mainLoop = function(tick) {
tick *= 0.001;
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.uniform1f(time_loc, tick);
window.requestAnimationFrame(mainLoop);
}
window.requestAnimationFrame(mainLoop);
<style>canvas {
width: 100%;
height: 500px;
}
</style>
<script id="vertexShader" type="x-shader/x-vertex">#version 300 es
precision mediump float;
in vec2 vertexCoord;
uniform float time;
smooth out vec2 pointCoord;
void main() {
gl_Position = vec4(vec2[](vertexCoord)[ gl_VertexID ], 0.0, 1.0);
// for testig purposes suffix likely to fail in the past on firefox 56 windows 7
pointCoord = vec3[3u](vec3(1.f, 1.f*sin(time), 0.f), vec3(0.0), vec3(0.0))[gl_VertexID%int(mod(time,3.))].xy;
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">#version 300 es
precision mediump float;
out vec4 outFragColor;
smooth in vec2 pointCoord; void main() {
outFragColor = vec4(pointCoord,0.0, 1.0);
}
</script>
<canvas id="canvas" style="width:100vw;height:100vh;display:block"></canvas>
My project has similar problem, later I found if I comment the gl.depthMask, there would be no flickering.
My final solution is close the development tool, the flicker will gone then.

Sending JavaScript variables to fragment shader

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>

Click to zoom in WebGL

I need to implement what amounts to a "Google Maps" style zoom effect in WebGL. Specifically I have a simple 2-dimensional scene that is always perpendicular to the camera. When a user clicks on the scene, the camera should zoom to a location that is over the click, but closer to the 2-dimensional scene.
For example see this jsfiddle, that implements the scene but no zooming:
http://jsfiddle.net/JqBs8/4/
If you have a WebGL enabled browser, you should see a triangle and a square (2-dimensional) rendered at -7 on the Z axis. I have put in a placeholder handleMouseUp() event handler that logs any click events, but I'm a little lost as to how to translate the coordinates given by the click event into a new location for the camera (or I guess equivalently a new view matrix).
(The jsfiddle is based on tutorial 1 from learningwebgl.com and uses the glMatrix http://code.google.com/p/glmatrix/ library for matrix operations. Keep in mind that this is WebGL, which is similar to OpenGL ES, and has no access to glu* functions.)
I've written something in this jsfiddle that should help you.
http://jsfiddle.net/hedzys6r/
(or https://codepen.io/brainjam/pen/gBZyGm)
Just click on the WebGL window to zoom in to where the mouse is pointing.
The basic idea is that a point in the WebGL window is obtained by projecting it from 3-space using the projection matrix pMatrix and the view matrix (the view matrix depends on where the camera is and the direction in which it is looking). The composition of these matrices is named pvMatrix in the code.
If you want the opposite transform from the window back to three space, you have to take a clip space coordinate (x,y,z) and 'unproject' it back into 3D using the inverse of pvMatrix. In clip space, coordinates are in the range [-1,1], and the z coordinate is depth.
In the OpenGL world, these transforms are implemented in gluProject() and gluUnproject() (which you can Google for more information and source code).
In the jsfiddle example, we calculate the (x,y) coordinates in clip space, and then unproject (x,y,z) for two different values of z. From that we get two points in 3D space that map onto (x,y), and we can infer a direction vector. We can then move the camera in that direction to get a zoom effect.
In the code, the camera position is at the negation of the eye vector.
This example shows you how to move the camera in the direction that you are clicking. If you want to actually move towards specific objects in the scene, you have to implement something like object selection, which is a different kettle of fish. The example I've given is unaware of the objects in the scene.
This is really part of brainjam's answer, but just in case that jsfiddle were to go away, I wanted to make sure the code was archived. Here is the primary bit:
function handleMouseUp(event) {
var world1 = [0,0,0,0] ;
var world2 = [0,0,0,0] ;
var dir = [0,0,0] ;
var w = event.srcElement.clientWidth ;
var h = event.srcElement.clientHeight ;
// calculate x,y clip space coordinates
var x = (event.offsetX-w/2)/(w/2) ;
var y = -(event.offsetY-h/2)/(h/2) ;
mat4.inverse(pvMatrix, pvMatrixInverse) ;
// convert clip space coordinates into world space
mat4.multiplyVec4(pvMatrixInverse, [x,y,-1,1], world1) ;
vec3.scale(world1,1/world1[3]) ;
mat4.multiplyVec4(pvMatrixInverse, [x,y,0,1], world2) ;
vec3.scale(world2,1/world2[3]) ;
// calculate world space view vector
vec3.subtract(world2,world1,dir) ;
vec3.normalize(dir) ;
vec3.scale(dir,0.3) ;
// move eye in direction of world space view vector
vec3.subtract(eye,dir) ;
drawScene();
console.log(event)
}
And the entirety of the JS...
var gl;
function initGL(canvas) {
try {
gl = canvas.getContext("experimental-webgl");
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
} catch (e) {
}
if (!gl) {
alert("Could not initialise WebGL, sorry :-(");
}
}
function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
var str = "";
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3) {
str += k.textContent;
}
k = k.nextSibling;
}
var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}
gl.shaderSource(shader, str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
var shaderProgram;
function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
}
var mvMatrix = mat4.create();
var pMatrix = mat4.create();
function setMatrixUniforms() {
gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix);
gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix);
}
var triangleVertexPositionBuffer;
var squareVertexPositionBuffer;
function initBuffers() {
triangleVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
var vertices = [
0.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
triangleVertexPositionBuffer.itemSize = 3;
triangleVertexPositionBuffer.numItems = 3;
squareVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
vertices = [
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
1.0, -1.0, 0.0,
-1.0, -1.0, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
squareVertexPositionBuffer.itemSize = 3;
squareVertexPositionBuffer.numItems = 4;
}
var eye = vec3.create([0,0,0]) ; // negation of actual eye position
var pvMatrix = mat4.create();
var pvMatrixInverse = mat4.create();
function drawScene() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
mat4.identity(mvMatrix);
// calculate the view transform mvMatrix, and the projection-view matrix pvMatrix
mat4.translate(mvMatrix, eye);
mat4.multiply(pMatrix,mvMatrix,pvMatrix) ;
mat4.translate(mvMatrix, [-1.5, 0.0, -7.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);
mat4.translate(mvMatrix, [3.0, 0.0, 0.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems);
}
function handleMouseUp(event) {
var world1 = [0,0,0,0] ;
var world2 = [0,0,0,0] ;
var dir = [0,0,0] ;
var w = event.srcElement.clientWidth ;
var h = event.srcElement.clientHeight ;
// calculate x,y clip space coordinates
var x = (event.offsetX-w/2)/(w/2) ;
var y = -(event.offsetY-h/2)/(h/2) ;
mat4.inverse(pvMatrix, pvMatrixInverse) ;
// convert clip space coordinates into world space
mat4.multiplyVec4(pvMatrixInverse, [x,y,-1,1], world1) ;
vec3.scale(world1,1/world1[3]) ;
mat4.multiplyVec4(pvMatrixInverse, [x,y,0,1], world2) ;
vec3.scale(world2,1/world2[3]) ;
// calculate world space view vector
vec3.subtract(world2,world1,dir) ;
vec3.normalize(dir) ;
vec3.scale(dir,0.3) ;
// move eye in direction of world space view vector
vec3.subtract(eye,dir) ;
drawScene();
console.log(event)
}
function webGLStart() {
var canvas = document.getElementById("lesson01-canvas");
initGL(canvas);
initShaders();
initBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.enable(gl.DEPTH_TEST);
canvas.onmouseup = handleMouseUp;
drawScene();
}
webGLStart();

Categories

Resources