I am creating a simple webgl program that puts 3 random vertices on the canvas and connects them into a triangle. I tried to add translation to move the triangle to the right (increase the X value of each vertex), but of course if it goes forever, the triangle will go out of the canvas. Does anyone know how to detect if the vertex has an x value above 1 and if yes, reset the position of the given vertex because my solution doesnt seem to do anything, its like it doesnt even trigger
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
gl.clearColor(0.1, 0.2, 0.2, 1.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
var indices = [0, 0, 0, 0, 0, 0];
for (var points = 0; points < 6; points++) {
indices[points] = (Math.random() * 2) - 1;
//indices[points + 1] = Math.random() < 0.5 ? -1 : 1;
}
var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(indices),
gl.STATIC_DRAW);
var vert = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vert, `
precision mediump float;
attribute vec2 position;
uniform vec2 translation;
void main(){
gl_Position = vec4(position + translation, 0.0, 1.0);
}
`);
gl.compileShader(vert);
var success1 = gl.getShaderParameter(vert, gl.COMPILE_STATUS);
if (!success1) {
// Something went wrong during compilation; get the error
throw gl.getShaderInfoLog(vert);
}
var frag = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(frag, `
precision mediump float;
void main(){
gl_FragColor = vec4(0.3, 0.6, 0.4, 1.0);
}
`);
gl.compileShader(frag);
var success2 = gl.getShaderParameter(frag, gl.COMPILE_STATUS);
if (!success2) {
// Something went wrong during compilation; get the error
throw gl.getShaderInfoLog(frag);
}
var program = gl.createProgram();
gl.attachShader(program, vert);
gl.attachShader(program, frag);
gl.linkProgram(program);
var vertLoc = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(vertLoc, 2, gl.FLOAT, gl.FALSE, 0, 0);
gl.enableVertexAttribArray(vertLoc);
gl.useProgram(program);
var trans = gl.getUniformLocation(program, "translation");
var translation = [0.0, 0.0];
gl.uniform2fv(trans, translation);
gl.drawArrays(gl.TRIANGLES, 0, 3);
function loop() {
gl.clearColor(0.1, 0.2, 0.2, 1.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
translation[0] += 0.01;
gl.uniform2fv(trans, translation);
gl.drawArrays(gl.TRIANGLES, 0, 3);
for (var points = 0; points < 6; points++) {
if (indices[points] % 2 == 0) {
if (indices[points] + translation[0] > 1) {
indices[points] = (Math.random() * 2) - 1;
}
}
//indices[points + 1] = Math.random() < 0.5 ? -1 : 1;
}
requestAnimationFrame(loop);
}
loop();
<canvas id="canvas"></canvas>
To achieve this, consider making the following changes to your code:
remove placement of vertices by translation in your vertex shader to give you "per-vertex" control over placement of the geometry (the translation effectivly means "object-level" placement which isn't what your want here)
when you iterate over the points in your loop(), you checking modulo over the vertex coordinate. You should be performing that check on the iteration index like this: if (points % 2 == 0)
now that the translation concept is gone, update the position of the vertex coordinate, rather the translation array after the modulo check: indices[points] += 0.01;
finally, seeing you're updating the indices vertex data, you'll need to update the webgl buf to ensure your changes are reflected when the next frame is rendered:
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(indices), gl.STATIC_DRAW);
Here's the updated script in full:
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
gl.clearColor(0.1, 0.2, 0.2, 1.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
var indices = [0, 0, 0, 0, 0, 0];
for (var points = 0; points < 6; points++) {
indices[points] = (Math.random() * 2) - 1;
}
var buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(indices),
gl.STATIC_DRAW);
var vert = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vert, `
precision mediump float;
attribute vec2 position;
void main(){
gl_Position = vec4(position, 0.0, 1.0);
}
`);
gl.compileShader(vert);
var success1 = gl.getShaderParameter(vert, gl.COMPILE_STATUS);
if (!success1) {
throw gl.getShaderInfoLog(vert);
}
var frag = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(frag, `
precision mediump float;
void main(){
gl_FragColor = vec4(0.3, 0.6, 0.4, 1.0);
}
`);
gl.compileShader(frag);
var success2 = gl.getShaderParameter(frag, gl.COMPILE_STATUS);
if (!success2) {
throw gl.getShaderInfoLog(frag);
}
var program = gl.createProgram();
gl.attachShader(program, vert);
gl.attachShader(program, frag);
gl.linkProgram(program);
var vertLoc = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(vertLoc, 2, gl.FLOAT, gl.FALSE, 0, 0);
gl.enableVertexAttribArray(vertLoc);
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);
function loop() {
gl.clearColor(0.1, 0.2, 0.2, 1.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
// Update the vertex data, causing the vertex x coordinate to increase per-frame
for (var points = 0; points < 6; points++) {
// Only process x coordinate
if (points % 2 == 0) {
// Increase x coordinate per-frame
indices[points] += 0.01;
// If x position > 1 reset it to a new random value
if (indices[points] > 1) {
indices[points] = (Math.random() * 2) - 1;
}
}
}
// Update webgl vertex buffer so that updated indices data is rendered
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(indices), gl.STATIC_DRAW);
requestAnimationFrame(loop);
}
loop();
<canvas id="canvas"><canvas>
Related
I have some streaming data, I want to draw interpolated vertices using gl.POINTS using WebGL2. I have smoothing function to produce circles and this is really what I need (I may want to interpolate twofold or threefold):
To illustrate problem:
To interpolate between vertices, here in JavaScript twofold:
var array = new Float32Array([-0.9, -0.9, 0.9, -0.9, 0.9, 0.9, -0.9, 0.9]);
var array2 = new Float32Array(array.length * 2 - 2);
/* init canvas */
var gl = document.getElementById('plot2').getContext('webgl2');
gl.canvas.width = gl.canvas.height = 128;
gl.viewportWidth = gl.canvas.width;
gl.viewportHeight = gl.canvas.height;
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
const fs_point = `#version 300 es
precision mediump float;
out vec4 color;
void main() {
color = vec4(0.5, 0., 0.5, 0.8);
}`;
const vs_points = `#version 300 es
precision mediump float;
in vec2 vPos;
void main() {
gl_Position = vec4(vPos.x, vPos.y, 0, 1.0);
gl_PointSize = 10.0;
}`;
const shaderProgram = gl.createProgram();
let shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, fs_point);
gl.compileShader(shader);
gl.attachShader(shaderProgram, shader);
shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, vs_points);
gl.compileShader(shader);
gl.attachShader(shaderProgram, shader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vPos");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
const pointPosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, pointPosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, array2, gl.DYNAMIC_DRAW);
pointPosBuffer.itemSize = 2;
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, pointPosBuffer.itemSize, gl.FLOAT, false, 0, 0);
/* /init canvas */
/* loop */
gl.clear(gl.COLOR_BUFFER_BIT);
//array changes, manual interpolation
for(var i = 0; i < array.length; i += 2) {
array2[2 * i] = array[i];
array2[2 * i + 1] = array[i + 1];
}
//here starts interpolation
for(var i = 2; i < array2.length - 2; i += 4) {
array2[i] = (array2[i - 2] + array2[i + 2]) * 0.5;
array2[i + 1] = (array2[i - 1] + array2[i + 3]) * 0.5;
}
gl.bufferData(gl.ARRAY_BUFFER, array2, gl.DYNAMIC_DRAW); //array values changes
gl.drawArrays(gl.GL_POINTS, 0, 7);
/* /loop */
<canvas id="plot2"></canvas>
It is really really awkward to do it manually, so here is switch to shader:
var array = new Float32Array([-0.9, -0.9, 0.9, -0.9, 0.9, 0.9, -0.9, 0.9]),
gl = document.getElementById('plot3').getContext('webgl2');
gl.canvas.width = gl.canvas.height = 128;
gl.viewportWidth = gl.canvas.width;
gl.viewportHeight = gl.canvas.height;
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
const fs_point = `#version 300 es
precision mediump float;
out vec4 color;
void main() {
color = vec4(0.5, 0., 0.5, 0.8);
}`;
const vs_points = `#version 300 es
precision mediump float;
in vec4 vPos;
void main() {
switch (gl_VertexID % 3) {
case 0:
gl_Position = vec4(vPos.x, vPos.y, 0, 1.0);
break;
case 1:
gl_Position = vec4(0.5 * (vPos.x + vPos.z), 0.5 * (vPos.y + vPos.w), 0, 1.0);
break;
case 2:
gl_Position = vec4(vPos.z, vPos.w, 0, 1.0);
break;
}
gl_PointSize = 12.0;
}`;
const shaderProgram = gl.createProgram();
let shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, fs_point);
gl.compileShader(shader);
gl.attachShader(shaderProgram, shader);
shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, vs_points);
gl.compileShader(shader);
gl.attachShader(shaderProgram, shader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
gl.clearColor(0.0, 0.0, 0.0, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.BLEND);
gl.disable(gl.DEPTH_TEST);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vPos");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
const pointPosBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, pointPosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW);
pointPosBuffer.itemSize = 4;
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, pointPosBuffer.itemSize, gl.FLOAT, false, 8, 0);
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
const indices = new Uint8Array([
0, 0, 0,
1, 1, 1,
2, 2, 2
]);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
/* /init canvas */
/* loop */
gl.clear(gl.COLOR_BUFFER_BIT);
//array changes, manual interpolation
gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW); //array values changes
gl.drawElements(gl.GL_POINTS, 9, gl.UNSIGNED_BYTE, 0);
/* /loop */
<canvas id="plot3"></canvas>
Here, I want to use indices and supply two pairs of x, y coordinates to shader. I see only three dots. I think the code tells the indices, calls three times the same quad of vertices interpolating them according to index (which seems to fail), advances 8 bytes (2 Float coordinates) and draws again.
Is there easier way to interpolate between vertices?
Aren't gl_VertexID or gl_InstanceID (tried the same at instanced version) suppose to advance every index / vertex?
Is this due to index reuse?
I'd like to be able to wrap an image of the Earth around my sphere. Using any stock image off of Google.
I've been looking around and I found only answers with three.js, and I'm looking for the most simple way to achieve my goal without that. Below is all of the documentation of a sphere I'd like to texture. If you copy and paste it you should be able to see a regular sphere with some shading on it.
From my WebGL book,
.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Point lighted sphere</title>
</head>
<body onload="main()">
<canvas id="webgl" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
<script src="../lib/webgl-utils.js"></script>
<script src="../lib/webgl-debug.js"></script>
<script src="../lib/cuon-utils.js"></script>
<script src="../lib/cuon-matrix.js"></script>
<script src="PointLightedSphere.js"></script>
</body>
</html>
.js
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
// 'attribute vec4 a_Color;\n' + // Defined constant in main()
'attribute vec4 a_Normal;\n' +
'uniform mat4 u_MvpMatrix;\n' +
'uniform mat4 u_ModelMatrix;\n' + // Model matrix
'uniform mat4 u_NormalMatrix;\n' + // Transformation matrix of the normal
'uniform vec3 u_LightColor;\n' + // Light color
'uniform vec3 u_LightPosition;\n' + // Position of the light source
'uniform vec3 u_AmbientLight;\n' + // Ambient light color
'varying vec4 v_Color;\n' +
'void main() {\n' +
' vec4 color = vec4(1.0, 1.0, 1.0, 1.0);\n' + // Sphere color
' gl_Position = u_MvpMatrix * a_Position;\n' +
// Calculate a normal to be fit with a model matrix, and make it 1.0 in length
' vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
// Calculate world coordinate of vertex
' vec4 vertexPosition = u_ModelMatrix * a_Position;\n' +
// Calculate the light direction and make it 1.0 in length
' vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n' +
// The dot product of the light direction and the normal
' float nDotL = max(dot(lightDirection, normal), 0.0);\n' +
// Calculate the color due to diffuse reflection
' vec3 diffuse = u_LightColor * color.rgb * nDotL;\n' +
// Calculate the color due to ambient reflection
' vec3 ambient = u_AmbientLight * color.rgb;\n' +
// Add the surface colors due to diffuse reflection and ambient reflection
' v_Color = vec4(diffuse + ambient, color.a);\n' +
'}\n';
// Fragment shader program
var FSHADER_SOURCE =
'#ifdef GL_ES\n' +
'precision mediump float;\n' +
'#endif\n' +
'varying vec4 v_Color;\n' +
'void main() {\n' +
' gl_FragColor = v_Color;\n' +
'}\n';
function main() {
// Retrieve <canvas> element
var canvas = document.getElementById('webgl');
// Get the rendering context for WebGL
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// Initialize shaders
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// Set the vertex coordinates, the color and the normal
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the vertex information');
return;
}
// Set the clear color and enable the depth test
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);
// Get the storage locations of uniform variables and so on
var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
if (!u_MvpMatrix || !u_NormalMatrix || !u_LightColor || !u_LightPosition || !u_AmbientLight) {
console.log('Failed to get the storage location');
return;
}
// Set the light color (white)
gl.uniform3f(u_LightColor, 0.8, 0.8, 0.8);
// Set the light direction (in the world coordinate)
gl.uniform3f(u_LightPosition, 5.0, 8.0, 7.0);
// Set the ambient light
gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);
var modelMatrix = new Matrix4(); // Model matrix
var mvpMatrix = new Matrix4();   // Model view projection matrix
var normalMatrix = new Matrix4(); // Transformation matrix for normals
// Pass the model matrix to u_ModelMatrix
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
// Calculate the view projection matrix
mvpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
mvpMatrix.lookAt(0, 0, 6, 0, 0, 0, 0, 1, 0);
mvpMatrix.multiply(modelMatrix);
// Pass the model view projection matrix to u_MvpMatrix
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
// Calculate the matrix to transform the normal based on the model matrix
normalMatrix.setInverseOf(modelMatrix);
normalMatrix.transpose();
// Pass the transformation matrix for normals to u_NormalMatrix
gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);
// Clear color and depth buffer
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Draw the cube(Note that the 3rd argument is the gl.UNSIGNED_SHORT)
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
}
function initVertexBuffers(gl) { // Create a sphere
var SPHERE_DIV = 13;
var i, ai, si, ci;
var j, aj, sj, cj;
var p1, p2;
var positions = [];
var indices = [];
// Generate coordinates
for (j = 0; j <= SPHERE_DIV; j++) {
aj = j * Math.PI / SPHERE_DIV;
sj = Math.sin(aj);
cj = Math.cos(aj);
for (i = 0; i <= SPHERE_DIV; i++) {
ai = i * 2 * Math.PI / SPHERE_DIV;
si = Math.sin(ai);
ci = Math.cos(ai);
positions.push(si * sj); // X
positions.push(cj); // Y
positions.push(ci * sj); // Z
}
}
// Generate indices
for (j = 0; j < SPHERE_DIV; j++) {
for (i = 0; i < SPHERE_DIV; i++) {
p1 = j * (SPHERE_DIV+1) + i;
p2 = p1 + (SPHERE_DIV+1);
indices.push(p1);
indices.push(p2);
indices.push(p1 + 1);
indices.push(p1 + 1);
indices.push(p2);
indices.push(p2 + 1);
}
}
// Write the vertex property to buffers (coordinates and normals)
// Same data can be used for vertex and normal
// In order to make it intelligible, another buffer is prepared separately
if (!initArrayBuffer(gl, 'a_Position', new Float32Array(positions), gl.FLOAT, 3)) return -1;
if (!initArrayBuffer(gl, 'a_Normal', new Float32Array(positions), gl.FLOAT, 3)) return -1;
// Unbind the buffer object
gl.bindBuffer(gl.ARRAY_BUFFER, null);
// Write the indices to the buffer object
var indexBuffer = gl.createBuffer();
if (!indexBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
return indices.length;
}
function initArrayBuffer(gl, attribute, data, type, num) {
// Create a buffer object
var buffer = gl.createBuffer();
if (!buffer) {
console.log('Failed to create the buffer object');
return false;
}
// Write date into the buffer object
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
// Assign the buffer object to the attribute variable
var a_attribute = gl.getAttribLocation(gl.program, attribute);
if (a_attribute < 0) {
console.log('Failed to get the storage location of ' + attribute);
return false;
}
gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
// Enable the assignment of the buffer object to the attribute variable
gl.enableVertexAttribArray(a_attribute);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
return true;
}
I have studying to WebGL technical. Unfortunately I met some error. I want to draw 2D two shape for helicopter. I wrote code below. Exactly what I would like to do is to make a propeller on a shape called a body.
Note that I tried to create a new buffer and bind it, but it still failed. I do not think stride has been understood yet, but I want to learn how to solve it even if it is simple.
// Vertex shader
var _vertexShader = `
attribute vec4 _position;
void main() {
gl_Position = _position;
}
`
// Fragment shader
var _fragmentShader = `
precision mediump float;
void main() {
gl_FragColor = vec4(0.6, 0.8, 0.3, 1);
}
`
// Create shader
function createShader(gl, type, source) {
var shader = gl.createShader(type)
gl.shaderSource(shader, source)
gl.compileShader(shader)
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
if (success) {
return shader
} else {
console.log(gl.getShaderInfoLog(shader))
gl.deleteShader(shader)
// return false
}
}
// Linking shader by program
function createProgram(gl, vertexShader, fragmentShader) {
var program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
var success = gl.getProgramParameter(program, gl.LINK_STATUS)
if (success) {
return program
} else {
console.log(gl.getProgramInfoLog(program))
gl.deleteProgram(program)
// return false
}
}
function main() {
// Get a WebGL context.
var canvas = document.getElementById('canvas')
var gl = canvas.getContext('webgl2')
if (!gl) {
alert('Not support.')
return false
}
// Get the strings for our GLSL shaders.
var vertexShader = createShader(gl, gl.VERTEX_SHADER, _vertexShader)
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, _fragmentShader)
// Link the two shaders into a program.
var program = createProgram(gl, vertexShader, fragmentShader)
// Look up where the vertex data need to go.
var positionLoc = gl.getAttribLocation(program, '_position')
// Create a buffer and put the three 2d clip space points in it.
var body_positionBuf = gl.createBuffer()
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = body_positionBuf).
gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
var body_positions = new Float32Array(
[
// Body triangle (Right)
-0.25, 0,
0.25, 0,
0, 0.5,
// Body triangle (Left)
-0.15, 0,
0.15, 0,
0.15, -0.25,
// Body square
-0.15, 0,
-0.15, -0.25,
0.15, -0.25,
]
)
var roter_positions = new Float32Array([
0.15, 0.1,
0.15, -0.1,
-0.15, 0.1,
-0.15, -0.1,
-0.15, 0.1,
0.15, 0.1,
])
gl.bufferData(gl.ARRAY_BUFFER, body_positions, gl.STATIC_DRAW)
// Code above this line is initialization code.
// Code below this line is rendering code.
// webglUtils.resizeCanvasToDisplaySize(gl.canvas)
// Tell WebGL how to convert from clip space to pixels.
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
// Clear the canvas.
gl.clearColor(0, 0, 0, 0)
gl.clear(gl.COLOR_BUFFER_BIT)
// Tell it to use our program (pair of shaders).
gl.useProgram(program)
// Turn on the attribute.
gl.enableVertexAttribArray(positionLoc)
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
// Tell the attribute how to get data out of body_positionBuf (ARRAY_BUFFER).
var size = 2
var type = gl.FLOAT
var normalize = false
var stride = 0
var offset = 0
gl.vertexAttribPointer(positionLoc, size, type, normalize, stride, offset)
// Draw.
var drawType = gl.TRIANGLES
var count = body_positions.length / 2
gl.drawArrays(drawType, offset, count)
}
main()
<canvas id="canvas" width="500" height="250"></canvas>
How can i do? I don't know.. Please help me thanks.
Fist of all the vertices coordinates which are specified in body_positions cover the vertices specified in roter_positions.
Change the vertex coordinates:
var roter_positions = new Float32Array([
0.15, 0.6,
0.15, 0.4,
-0.15, 0.6,
-0.15, 0.4,
-0.15, 0.6,
0.15, 0.6,
])
Note, I recommend to use a model matrix, to define the position and orientation of each object individually. But solve your issue first and then take the next step.
If you wan to use 2 buffers, then you have to create 2 buffer objects and you have to define the data store of both objects:
var body_positionBuf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
gl.bufferData(gl.ARRAY_BUFFER, body_positions, gl.STATIC_DRAW)
var roter_positionBuf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, roter_positionBuf)
gl.bufferData(gl.ARRAY_BUFFER, roter_positions, gl.STATIC_DRAW)
Finally you can draw the meshes in the 2 separated buffers, by 2 separated draw calls. You have to define the array of generic vertex attribute data before each draw call:
gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)
gl.drawArrays(gl.TRIANGLES, 0, body_positions.length / 2)
gl.bindBuffer(gl.ARRAY_BUFFER, roter_positionBuf)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)
gl.drawArrays(gl.TRIANGLES, 0, roter_positions.length / 2)
See the example, eher I applied the changes to your original code:
// Vertex shader
var _vertexShader = `
attribute vec4 _position;
void main() {
gl_Position = _position;
}
`
// Fragment shader
var _fragmentShader = `
precision mediump float;
void main() {
gl_FragColor = vec4(0.6, 0.8, 0.3, 1);
}
`
// Create shader
function createShader(gl, type, source) {
var shader = gl.createShader(type)
gl.shaderSource(shader, source)
gl.compileShader(shader)
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
if (success) {
return shader
} else {
console.log(gl.getShaderInfoLog(shader))
gl.deleteShader(shader)
// return false
}
}
// Linking shader by program
function createProgram(gl, vertexShader, fragmentShader) {
var program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
var success = gl.getProgramParameter(program, gl.LINK_STATUS)
if (success) {
return program
} else {
console.log(gl.getProgramInfoLog(program))
gl.deleteProgram(program)
// return false
}
}
function main() {
// Get a WebGL context.
var canvas = document.getElementById('canvas')
var gl = canvas.getContext('webgl2')
if (!gl) {
alert('Not support.')
return false
}
// Get the strings for our GLSL shaders.
var vertexShader = createShader(gl, gl.VERTEX_SHADER, _vertexShader)
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, _fragmentShader)
// Link the two shaders into a program.
var program = createProgram(gl, vertexShader, fragmentShader)
// Look up where the vertex data need to go.
var positionLoc = gl.getAttribLocation(program, '_position')
// Create a buffer and put the three 2d clip space points in it.
var body_positions = new Float32Array(
[
// Body triangle (Right)
-0.25, 0,
0.25, 0,
0, 0.5,
// Body triangle (Left)
-0.15, 0,
0.15, 0,
0.15, -0.25,
// Body square
-0.15, 0,
-0.15, -0.25,
0.15, -0.25,
]
)
var roter_positions = new Float32Array([
0.15, 0.6,
0.15, 0.4,
-0.15, 0.6,
-0.15, 0.4,
-0.15, 0.6,
0.15, 0.6,
])
var body_positionBuf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
gl.bufferData(gl.ARRAY_BUFFER, body_positions, gl.STATIC_DRAW)
var roter_positionBuf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, roter_positionBuf)
gl.bufferData(gl.ARRAY_BUFFER, roter_positions, gl.STATIC_DRAW)
// webglUtils.resizeCanvasToDisplaySize(gl.canvas)
// Tell WebGL how to convert from clip space to pixels.
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
// Clear the canvas.
gl.clearColor(0, 0, 0, 0)
gl.clear(gl.COLOR_BUFFER_BIT)
// Tell it to use our program (pair of shaders).
gl.useProgram(program)
// Turn on the attribute.
gl.enableVertexAttribArray(positionLoc)
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, body_positionBuf)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)
gl.drawArrays(gl.TRIANGLES, 0, body_positions.length / 2)
gl.bindBuffer(gl.ARRAY_BUFFER, roter_positionBuf)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)
gl.drawArrays(gl.TRIANGLES, 0, roter_positions.length / 2)
}
main();
<canvas id="canvas" width="500" height="250"></canvas>
Of course you can add the 2 arrays to 1 buffer. You have to create a buffer with a data store, which is large enough for both arrays. Add the data to the buffer by gl.bufferData:
var body_bytes = body_positions.length * 4;
var roter_bytes = roter_positions.length * 4;
var common_positionBuf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, common_positionBuf)
gl.bufferData(gl.ARRAY_BUFFER, body_bytes + roter_bytes, gl.STATIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, body_positions);
gl.bufferSubData(gl.ARRAY_BUFFER, body_bytes, roter_positions);
The mesh can be draw by a singel draw call:
gl.bindBuffer(gl.ARRAY_BUFFER, common_positionBuf)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)
var no_of_vertices = (body_positions.length + roter_positions.length) / 2
gl.drawArrays(gl.TRIANGLES, 0, no_of_vertices)
If some program resources would have to be changed, before the 2nd mesh is draw (e.g. model matrix uniform), then this can be done by 2 draw calls too:
gl.drawArrays(gl.TRIANGLES, 0, body_positions.length / 2)
// change resources here ...
gl.drawArrays(gl.TRIANGLES, body_positions.length / 2, roter_positions.length / 2)
See the example, based on your original code from the question:
// Vertex shader
var _vertexShader = `
attribute vec4 _position;
void main() {
gl_Position = _position;
}
`
// Fragment shader
var _fragmentShader = `
precision mediump float;
void main() {
gl_FragColor = vec4(0.6, 0.8, 0.3, 1);
}
`
// Create shader
function createShader(gl, type, source) {
var shader = gl.createShader(type)
gl.shaderSource(shader, source)
gl.compileShader(shader)
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
if (success) {
return shader
} else {
console.log(gl.getShaderInfoLog(shader))
gl.deleteShader(shader)
// return false
}
}
// Linking shader by program
function createProgram(gl, vertexShader, fragmentShader) {
var program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
var success = gl.getProgramParameter(program, gl.LINK_STATUS)
if (success) {
return program
} else {
console.log(gl.getProgramInfoLog(program))
gl.deleteProgram(program)
// return false
}
}
function main() {
// Get a WebGL context.
var canvas = document.getElementById('canvas')
var gl = canvas.getContext('webgl2')
if (!gl) {
alert('Not support.')
return false
}
// Get the strings for our GLSL shaders.
var vertexShader = createShader(gl, gl.VERTEX_SHADER, _vertexShader)
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, _fragmentShader)
// Link the two shaders into a program.
var program = createProgram(gl, vertexShader, fragmentShader)
// Look up where the vertex data need to go.
var positionLoc = gl.getAttribLocation(program, '_position')
// Create a buffer and put the three 2d clip space points in it.
var body_positions = new Float32Array(
[
// Body triangle (Right)
-0.25, 0,
0.25, 0,
0, 0.5,
// Body triangle (Left)
-0.15, 0,
0.15, 0,
0.15, -0.25,
// Body square
-0.15, 0,
-0.15, -0.25,
0.15, -0.25,
]
)
var roter_positions = new Float32Array([
0.15, 0.6,
0.15, 0.4,
-0.15, 0.6,
-0.15, 0.4,
-0.15, 0.6,
0.15, 0.6,
])
var body_bytes = body_positions.length * 4;
var roter_bytes = roter_positions.length * 4;
var common_positionBuf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, common_positionBuf)
gl.bufferData(gl.ARRAY_BUFFER, body_bytes + roter_bytes, gl.STATIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, body_positions);
gl.bufferSubData(gl.ARRAY_BUFFER, body_bytes, roter_positions);
// webglUtils.resizeCanvasToDisplaySize(gl.canvas)
// Tell WebGL how to convert from clip space to pixels.
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
// Clear the canvas.
gl.clearColor(0, 0, 0, 0)
gl.clear(gl.COLOR_BUFFER_BIT)
// Tell it to use our program (pair of shaders).
gl.useProgram(program)
// Turn on the attribute.
gl.enableVertexAttribArray(positionLoc)
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, common_positionBuf)
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0)
var no_of_vertices = (body_positions.length + roter_positions.length) / 2
gl.drawArrays(gl.TRIANGLES, 0, no_of_vertices)
}
main();
<canvas id="canvas" width="500" height="250"></canvas>
I've recently started learning Javascript/ WebGL and I've learned enough to put together a simple Mandelbrot fractal renderer. The program works fine but for some reason it won't let me zoom in more than about 20 times, and it starts looking pixellated if I zoom in more. I've had this problem before in other fractal drawing programs I've made, but it usually doesn't become noticeable until about 2^45 zoom. I was thinking maybe it has to do with the max float size in GLSL, but I'm really not sure what the problem is or even how to go about finding the problem. I was just wondering if anyone knows what the cause of this zoom limit is and if there's any way I can increase it? Here's my HTML/ GLSL code:
<html>
<head>
<title>Mandelbrot Set</title>
<style>
body {
margin = 0;
padding = 0;
}
</style>
</head>
<body>
<h3>Click on the fractal to zoom in.</h3>
<canvas id = "canvas" width = "500" height = "500" onclick = "drawFractal();">
Sorry, your browser does not support HTML5.
</canvas>
<script id = "vertexshader" type = "vertexshader">
attribute vec2 a_position;
void main(){
gl_Position = vec4(a_position, 0, 0);
}
</script>
<script id = "fragmentshader" type = "fragmentshader">
precision mediump float;
uniform vec2 u_resolution;
uniform vec2 u_zoomCenter;
uniform float u_zoom;
uniform int u_maxIterations;
uniform float u_colorDiversity;
vec2 f(vec2 z, vec2 c)
{
return vec2(z.x*z.x - z.y*z.y, z.x*z.y*2.0) + c;
}
// Credit to hughsk on GitHub for this hsv to rgb converter
vec3 hsv2rgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main(){
vec2 zeroToOne = gl_FragCoord.xy / u_resolution;
vec2 c = u_zoomCenter + (zeroToOne * 4.0 - vec2(2.0)) / u_zoom;
vec2 z = vec2(0.0);
bool escaped = false;
float iterations = 0.0;
for (int i = 0; i < 100000; i++)
{
if (i > u_maxIterations) break;
z = f(z, c);
if (length(z) > 2.0)
{
escaped = true;
iterations = float(i);
break;
}
}
gl_FragColor = escaped ? vec4(hsv2rgb(vec3(iterations * u_colorDiversity, 1.0, 1.0)), 1.0) : vec4(vec3(0.0), 1.0);
}
</script>
<script src = "webgl.js"></script>
</body>
</html>
Here's my "webgl.js" file:
// Compile and link shaders and create program
function createShader(gl, type, source){
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return shader;
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
alert("Error: failed to create shader. Check the console for more information.");
}
function createProgram(gl, vertexShader, fragmentShader){
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (gl.getProgramParameter(program, gl.LINK_STATUS)) return program;
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
alert("Error: failed to create program. Check the console for more information.");
}
// WebGL setup
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
if (!gl){
var gl = canvas.getContext("experimental-webgl");
console.log("WebGL not supported, falling back on experimental WebGL.");
}
if (!gl){
console.log("Experimental WebGL not supported.");
alert("Your browser does not support WebGL. Check the console for more information.");
}
// Create shaders and program
var vertexShaderSource = document.getElementById("vertexshader").text;
var fragmentShaderSource = document.getElementById("fragmentshader").text;
var vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
var program = createProgram(gl, vertexShader, fragmentShader);
gl.useProgram(program);
// Set up position buffer
var screen = new Float32Array([
-1, -1,
1, -1,
1, 1,
1, 1,
-1, 1,
-1, -1]);
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, screen, gl.STATIC_DRAW);
// Set up position attribute in vertex shader
var a_positionLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray(a_positionLocation);
gl.vertexAttribPointer(a_positionLocation, 2, gl.FLOAT, false, 0, 0);
// Set up WebGL window
gl.viewport(0, 0, 500, 500);
gl.clearColor(0, 0, 0, 0);
// Set up uniforms in fragment shader
var u_resolutionLocation = gl.getUniformLocation(program, "u_resolution");
var u_zoomCenterLocation = gl.getUniformLocation(program, "u_zoomCenter");
var u_zoomLocation = gl.getUniformLocation(program, "u_zoom");
var u_maxIterationsLocation = gl.getUniformLocation(program, "u_maxIterations");
var u_colorDiversityLocation = gl.getUniformLocation(program, "u_colorDiversity");
gl.uniform2f(u_resolutionLocation, 500, 500);
// Set up some global variables
var offset_x = 0;
var offset_y = 0;
var zoom = 1;
var iterations = 10000;
var colorDiversity = 0.01;
// Update uniforms based on global variables
function updateUniforms()
{
gl.uniform2f(u_zoomCenterLocation, offset_x, offset_y);
gl.uniform1f(u_zoomLocation, zoom);
gl.uniform1i(u_maxIterationsLocation, iterations);
gl.uniform1f(u_colorDiversityLocation, colorDiversity);
}
// Get mouse position
function getMousePos() {
var rect = canvas.getBoundingClientRect();
return [(event.clientX - rect.left - 250) / 125, (event.clientY - rect.top - 250) / 125];
}
// Draw the fractal
function drawFractal() {
mousePos = getMousePos();
offset_x += mousePos[0] / zoom;
offset_y -= mousePos[1] / zoom;
zoom *= 2;
updateUniforms();
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
// Draw fractal when the page loads
updateUniforms();
gl.drawArrays(gl.TRIANGLES, 0, 6);
Maximal possible zoom depends on the precision of the floating point number you use and your algorithm.
You can increase precision using arbitrary precision numbers, for example mpfr, mpc or arb library
I am able to draw a circle
I want to pick a arbitary point on circle and draw a shape like a triangle or
a simple point of the circumference
Now what I am understanding is vertexData array has that points
so I can pick a point from vertexData
But, how do I proceed with drawing the point to that location
If its only about drawing a point on canvas
I understand that in vertexShader I can
declare
attribute vec4 a_Position
and then gl_Position = a_Position
but on circumference of circle I am not understanding
please guide here
Thanks
<script>
var vertexShaderText = [
'uniform vec2 u_resolution;',
'',
'attribute vec2 a_position;',
'',
'void main()',
'{',
'',
'vec2 clipspace = a_position / u_resolution * 1.0 ;',
'',
'gl_Position = vec4(clipspace * vec2(1, -1), 0, 1);',
'}'
].join("\n");
var fragmentShaderText = [
'precision mediump float;',
'',
'void main(void)',
'{',
'',
'gl_FragColor = vec4(1.0, 0, 0, 0);',
'',
'}'
].join("\n");
var uni = function(){
var canvas = document.getElementById("game-surface");
var gl = canvas.getContext("webgl",{antialias: true});
console.log("This is working");
gl.clearColor(0.412,0.412,0.412,1);
gl.clear(gl.COLOR_BUFFER_BIT);
var vertextShader = gl.createShader(gl.VERTEX_SHADER);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertextShader,vertexShaderText);
gl.shaderSource(fragmentShader,fragmentShaderText);
gl.compileShader(vertextShader);
gl.compileShader(fragmentShader);
if(!gl.getShaderParameter(vertextShader,gl.COMPILE_STATUS)){
console.error("Error with vertexshader",gl.getShaderInfoLog(vertextShader));
return;
}
if(!gl.getShaderParameter(fragmentShader,gl.COMPILE_STATUS)){
console.error("Error with fragmentShader",gl.getShaderInfoLog(fragmentShader));
return;
}
var program =gl.createProgram();
gl.attachShader(program,vertextShader);
gl.attachShader(program,fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
if(!gl.getProgramParameter(program,gl.LINK_STATUS)){
console.error("Error linking program",gl.getProgramInfoLog(program));
return;
}
gl.validateProgram(program);
if(!gl.getProgramParameter(program,gl.VALIDATE_STATUS)){
console.error("Error validating",gl.getProgramInfoLog(program));
}
var circle = {x: 0, y:0, r: 500};
var ATTRIBUTES = 2;
var numFans = 64;
var degreePerFan = (2* Math.PI) / numFans;
var vertexData = [circle.x, circle.y];
// console.log(gl_Position)
for(var i = 0; i <= numFans; i++) {
var index = ATTRIBUTES * i + 2; // there is already 2 items in array
var angle = degreePerFan * (i+0.1);
//console.log(angle)
vertexData[index] = circle.x + Math.cos(angle) * circle.r;
vertexData[index + 1] = circle.y + Math.sin(angle) * circle.r;
}
//console.log(vertexData);
var vertexDataTyped = new Float32Array(vertexData);
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexDataTyped, gl.STATIC_DRAW);
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
gl.enableVertexAttribArray(positionLocation);
var positionLocation = gl.getAttribLocation(program, "a_position");
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0);
gl.drawArrays(gl.TRIANGLE_FAN, 0, vertexData.length/ATTRIBUTES);
};
uni();
</script>
You're using a triangle fan to draw this circle, so drawing an additional shape requires a second draw call. This isn't going to scale well, since draw calls are expensive, more likely you're going to want some method to draw multiple shapes in a single draw call.
That said, as a simple example, you can add the following code to the bottom of your uni function, after the end of the first draw call at the end of the existing function, to place a second, smaller circle on the circumference of the first one using a second draw call. Given your fragment shader, this will also be a red circle, so you may want to modify the shader to use a different color.
// Insert this code at the end of the uni() function, it will make
// use of variables and GL state already declared earlier in that function.
// Pick a point along circumference, range 1 to 63
var selectedPointIndex = 8;
circle.x = vertexData[selectedPointIndex * 2];
circle.y = vertexData[selectedPointIndex * 2 + 1];
circle.r = 50;
vertexData = [circle.x, circle.y];
for(var i = 0; i <= numFans; i++) {
var index = ATTRIBUTES * i + 2; // there is already 2 items in array
var angle = degreePerFan * (i+0.1);
vertexData[index] = circle.x + Math.cos(angle) * circle.r;
vertexData[index + 1] = circle.y + Math.sin(angle) * circle.r;
}
vertexDataTyped = new Float32Array(vertexData);
buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexDataTyped, gl.STATIC_DRAW);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0);
gl.drawArrays(gl.TRIANGLE_FAN, 0, vertexData.length/ATTRIBUTES);