I need help figuring out how to add textures to my basketball.
What should i add to my vertex and fragment shaders.
Fragment shader
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
struct MaterialProperties {
vec4 diffuseColor;
vec3 specularColor;
vec3 emissiveColor;
float specularExponent;
};
struct LightProperties {
bool enabled;
vec4 position;
vec3 color;
};
uniform MaterialProperties material; // do two-sided lighting, but assume front and back materials are the same
uniform LightProperties lights[4];
uniform mat3 normalMatrix;
varying vec3 v_normal;
varying vec3 v_eyeCoords;
vec3 lightingEquation( LightProperties light, MaterialProperties material,
vec3 eyeCoords, vec3 N, vec3 V ) {
// N is normal vector, V is direction to viewer.
vec3 L, R; // Light direction and reflected light direction.
if ( light.position.w == 0.0 ) {
L = normalize( light.position.xyz );
}
else {
L = normalize( light.position.xyz/light.position.w - v_eyeCoords );
}
if (dot(L,N) <= 0.0) {
return vec3(0.0);
}
vec3 reflection = dot(L,N) * light.color * material.diffuseColor.rgb;
R = -reflect(L,N);
if (dot(R,V) > 0.0) {
float factor = pow(dot(R,V),material.specularExponent);
reflection += factor * material.specularColor * light.color;
}
return reflection;
}
void main() {
vec3 normal = normalize( normalMatrix*v_normal );
vec3 viewDirection = normalize( -v_eyeCoords); // (Assumes a perspective projection.)
vec3 color = material.emissiveColor;
for (int i = 0; i < 4; i++) {
if (lights[i].enabled) {
if (gl_FrontFacing) {
color += lightingEquation( lights[i], material, v_eyeCoords,
normal, viewDirection);
}
else {
color += lightingEquation( lights[i], material, v_eyeCoords,
-normal, viewDirection);
}
}
}
gl_FragColor = vec4(color,material.diffuseColor.a);
}
The function "world()" is being draw in my "Draw ()" function. The world consist of a scene, but for the sake of readability i will just like the ball i want to add texture to.
function world() {
// Ground
pushMatrix();
mat4.translate(modelview,modelview,[-1,-3,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[9,9,0.5]);
currentColor = [0.0,0.7,0.0,1];
cube.render();
popMatrix();
//Basketball
pushMatrix();
mat4.translate(modelview,modelview,[xval,yval,3]);//
mat4.scale(modelview,modelview,[0.15,0.15,0.15]);
currentColor = [1,0.3,0,1];
sphere.render();
popMatrix();
}
I have a createModel function that holds all my buffers.
I have a createProgram function that creates a program using the vertex and fragment shaders.
I have a initGL function that initializes the webgl context and gets attributes and uniform locations.
I have a init function that is called when the page is loaded.
Complete version of my code:
<!DOCTYPE html>
<meta charset="UTF-8">
<html>
<!--
A demo of lighting and hierarchical modeling using WebGL.
Emissive color is used to make the lights seem to glow yellow when
they are turned on.
Derived from code available from Diskworld-2.html
The Shaders are still complex but have been simplified slightly.
-->
<head>
<title>Project 4 - Shooting basketball</title>
<style>
body {
background-color: #EEEEEE;
}
label {
white-space: pre;
margin-left: 25px;
}
</style>
<!-- This shader is exactly as provided -->
<script type="x-shader/x-vertex" id="vshader-source">
attribute vec3 a_coords;
attribute vec3 a_normal;
uniform mat4 modelview;
uniform mat4 projection;
varying vec3 v_normal;
varying vec3 v_eyeCoords;
void main() {
vec4 coords = vec4(a_coords,1.0);
vec4 eyeCoords = modelview * coords;
gl_Position = projection * eyeCoords;
v_normal = normalize(a_normal);
v_eyeCoords = eyeCoords.xyz/eyeCoords.w;
}
</script>
<!-- This shader has simplified lighting model and equation
Notice the light only has 3 properties now -->
<script type="x-shader/x-fragment" id="fshader-source">
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
struct MaterialProperties {
vec4 diffuseColor;
vec3 specularColor;
vec3 emissiveColor;
float specularExponent;
};
struct LightProperties {
bool enabled;
vec4 position;
vec3 color;
};
uniform MaterialProperties material; // do two-sided lighting, but assume front and back materials are the same
uniform LightProperties lights[4];
uniform mat3 normalMatrix;
varying vec3 v_normal;
varying vec3 v_eyeCoords;
vec3 lightingEquation( LightProperties light, MaterialProperties material,
vec3 eyeCoords, vec3 N, vec3 V ) {
// N is normal vector, V is direction to viewer.
vec3 L, R; // Light direction and reflected light direction.
if ( light.position.w == 0.0 ) {
L = normalize( light.position.xyz );
}
else {
L = normalize( light.position.xyz/light.position.w - v_eyeCoords );
}
if (dot(L,N) <= 0.0) {
return vec3(0.0);
}
vec3 reflection = dot(L,N) * light.color * material.diffuseColor.rgb;
R = -reflect(L,N);
if (dot(R,V) > 0.0) {
float factor = pow(dot(R,V),material.specularExponent);
reflection += factor * material.specularColor * light.color;
}
return reflection;
}
void main() {
vec3 normal = normalize( normalMatrix*v_normal );
vec3 viewDirection = normalize( -v_eyeCoords); // (Assumes a perspective projection.)
vec3 color = material.emissiveColor;
for (int i = 0; i < 4; i++) {
if (lights[i].enabled) {
if (gl_FrontFacing) {
color += lightingEquation( lights[i], material, v_eyeCoords,
normal, viewDirection);
}
else {
color += lightingEquation( lights[i], material, v_eyeCoords,
-normal, viewDirection);
}
}
}
gl_FragColor = vec4(color,material.diffuseColor.a);
}
</script>
<script src="gl-matrix-min.js"></script>
<script src="trackball-rotator.js"></script>
<script src="basic-object-models-IFS.js"></script>
<script>
"use strict";
var gl; // The webgl context.
var xval= 1.3;
var yval= -1.8;
var kval = 0; // Kick leg start
var a_coords_loc; // Location of the a_coords attribute variable in the shader program.
var a_normal_loc; // Location of a_normal attribute
var a_texCoords_loc;
// var texture;
var u_modelview; // Locations for uniform matrices
var u_projection;
var u_normalMatrix;
var u_material; // An object tolds uniform locations for the material.
var u_lights; // An array of objects that holds uniform locations for light properties.
var projection = mat4.create(); // projection matrix
var modelview; // modelview matrix; value comes from rotator
var normalMatrix = mat3.create(); // matrix, derived from modelview matrix, for transforming normal vectors
var rotator; // A TrackballRotator to implement rotation by mouse.
var frameNumber = 0; // frame number during animation (actually only goes up by 0.5 per frame)
var torus, sphere, cone, cylinder, disk, ring, cube; // basic objects, created using function createModel
var matrixStack = []; // A stack of matrices for implementing hierarchical graphics.
var currentColor = [1,1,1,1]; // The current diffuseColor; render() functions in the basic objects set
// the diffuse color to currentColor when it is called before drawing the object.
// Other color properties, which don't change often are handled elsewhere.
var textureURLs = [
"textures/basketball.jpg" // array to hold textures
];
/**
* Draws the image, which consists of either the "world" or a closeup of the "car".
*/
function draw() {
gl.clearColor(0,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(projection, Math.PI/4, 1, 1, 50);
gl.uniformMatrix4fv(u_projection, false, projection );
modelview = rotator.getViewMatrix();
lights();
world();
}
function setLightPosition( u_position_loc, modelview, lightPosition ) {
var transformedPosition = new Float32Array(4);
vec4.transformMat4(transformedPosition, lightPosition, modelview);
gl.uniform4fv(u_position_loc, transformedPosition);
}
function lights() {
gl.uniform1i( u_lights[0].enabled, 1 );
gl.uniform4f( u_lights[0].position, 0,0,1,0 );
gl.uniform3f( u_lights[0].color, 0.15,0.15,0.15 );
// Sun light
gl.uniform1i( u_lights[1].enabled, 1 );
gl.uniform3f( u_lights[1].color, 1,0.3,0.0 );
//Lamp at night
gl.uniform1i( u_lights[2].enabled, 1 );
gl.uniform3f( u_lights[2].color, 0.35, 0.35, 0.1 ); // light 1 is the lamp at night
gl.uniform1f( u_lights[2].attenuation, 2 );
gl.uniform1i( u_lights[3].enabled, 1 );
gl.uniform3f( u_lights[3].color, 0.35, 0.35, 0.1 ); // light 1 is the lamp at night
gl.uniform1f( u_lights[3].attenuation, 2 );
currentColor = [ 0.3, 0.3, 0.3, 1 ];
//SunSetlight
pushMatrix();
mat4.translate(modelview,modelview,[7,-3,-10]);//
mat4.scale(modelview,modelview,[0.8,0.8,0.8]);
setLightPosition(u_lights[1].position, modelview, [1,0,0,0]);
gl.uniform3f( u_material.emissiveColor, 1.5,0.4, 0 );
sphere.render();
gl.uniform3f( u_material.emissiveColor, 0, 0, 0 );
popMatrix();
// lamp for night
pushMatrix();
mat4.translate(modelview,modelview,[-1,-0.8,3]);
mat4.scale(modelview,modelview,[0.4,0.1,0.1]);
setLightPosition(u_lights[2].position, modelview, [0,0,0,1]);
gl.uniform3f( u_material.emissiveColor, 1, 1, 0 );
cube.render();
gl.uniform3f( u_material.emissiveColor, 0, 0, 0 );
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-1,-0.8,-3]);
mat4.scale(modelview,modelview,[0.4,0.1,0.1]);
setLightPosition(u_lights[3].position, modelview, [0,0,0,1]);
gl.uniform3f( u_material.emissiveColor, 1, 1, 0 );
cube.render();
gl.uniform3f( u_material.emissiveColor, 0, 0, 0 );
popMatrix();
}
/**
* Draws a boxman figure using transformation from cubes and cylinders.
* Note for every push their should be a pop. This resets the transformations
*/
function world() {
// Ground
pushMatrix();
mat4.translate(modelview,modelview,[-1,-3,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[9,9,0.5]);
currentColor = [0.0,0.7,0.0,1];
cube.render();
popMatrix();
// Street lamp
pushMatrix();
mat4.translate(modelview,modelview,[-0.8,-4.7,3]);
mat4.scale(modelview,modelview,[0.5,0.5,0.5]);
streetLight();
popMatrix();
// Street lamp
pushMatrix();
mat4.translate(modelview,modelview,[-0.8,-4.7,-3]);
mat4.scale(modelview,modelview,[0.5,0.5,0.5]);
streetLight();
popMatrix();
// Basketball court
pushMatrix();
mat4.translate(modelview,modelview,[2,-2.75,3]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[3,3,0.1]);
currentColor = [0.2,0.2,0.2,1];
cube.render();
popMatrix();
//Road
pushMatrix();
mat4.translate(modelview,modelview,[-2,-2.75,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[2,9,0.1]);
currentColor = [0.2,0.2,0.2,1];
cube.render();
popMatrix();
//Road
pushMatrix();
mat4.translate(modelview,modelview,[-2,-2.72,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.2,9,0.1]);
currentColor = [1,1,1,1];
cube.render();
popMatrix();
//Basketball
pushMatrix();
mat4.translate(modelview,modelview,[xval,yval,3]);//
mat4.scale(modelview,modelview,[0.15,0.15,0.15]);
currentColor = [1,0.3,0,1];
sphere.render();
popMatrix();
// BasketBall Player
// Player 1
pushMatrix();
mat4.translate(modelview,modelview,[1,-2.2,3]);
mat4.scale(modelview,modelview,[0.15,0.15,0.15]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[0,1,0]);
// Arms
pushMatrix();
mat4.translate(modelview,modelview,[0,-0.5,-2]);
mat4.rotate(modelview,modelview,(-45)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.5,3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-2,-0.5,-2]);
mat4.rotate(modelview,modelview,(-45)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.5,3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
player();
popMatrix();
// PLayer 2
pushMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[2.4,-2.2,3.2]);
mat4.scale(modelview,modelview,[0.2,0.2,0.2]);
mat4.rotate(modelview,modelview,(-90)/180*Math.PI,[0,1,0]);
// Arms
pushMatrix();
mat4.translate(modelview,modelview,[0,1.2,-3]);
mat4.rotate(modelview,modelview,(kval)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.5,3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-2,1.2,-3]);
mat4.rotate(modelview,modelview,(kval)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.5,3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
player();
popMatrix();
//baskethoop
pushMatrix();
mat4.translate(modelview,modelview,[3,-4.3,3]);
mat4.scale(modelview,modelview,[0.5,0.5,0.5]);
basketballhoop();
popMatrix();
//Houses
pushMatrix();
mat4.translate(modelview,modelview,[-4,-2,2]);
mat4.scale(modelview,modelview,[0.6,0.6,0.6]);
house();
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-4,-2,-2.3]);
mat4.scale(modelview,modelview,[0.6,0.6,0.6]);
house();
popMatrix();
}
function streetLight(){
pushMatrix();
mat4.translate(modelview,modelview,[0,0,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[-1,0,0]);
mat4.scale(modelview,modelview,[0.2,0.2,4]);
cylinder.render();
popMatrix();
}
function house(){
pushMatrix();
currentColor = [0.5,0.4,0.2,1];
mat4.scale(modelview,modelview,[2,2,2]);
cube.render();
popMatrix();
pushMatrix();
currentColor = [0.5,0.4,0.2,1];
mat4.translate(modelview,modelview,[0,-0.5,1.6]);
mat4.scale(modelview,modelview,[1.3,1.3,1.3]);
cube.render();
popMatrix();
pushMatrix();
currentColor = [0.2,0.2,0.2,1];
mat4.translate(modelview,modelview,[0,1,0.0]);
mat4.scale(modelview,modelview,[4,1.5,4]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[-1,0,0]);
cone.render();
popMatrix();
}
function basketballhoop(){
pushMatrix();
currentColor = [0.1,0.1,0.1,1];
mat4.translate(modelview,modelview,[0,0,0]);
mat4.scale(modelview,modelview,[0.2,3,0.2]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[-1,0,0]);
cylinder.render();
popMatrix();
pushMatrix();
currentColor = [0.5,0.5,0.5,1];
mat4.scale(modelview,modelview,[0.15,0.15,0.15]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[-1,0,0]);
mat4.translate(modelview,modelview,[-4.5,0,43]);
ring.render();
popMatrix();
pushMatrix();
currentColor = [0,0,0.5,1];
mat4.scale(modelview,modelview,[0.1,1.5,2]);
mat4.translate(modelview,modelview,[0,4.4,0]);
cube.render();
popMatrix();
}
function player(){
pushMatrix();
//Head
mat4.translate(modelview,modelview,[-1,3,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.5,0.4,0.8]);
currentColor = [0.7,0.4,0.1,1];
sphere.render();
popMatrix();
// Neck
pushMatrix();
mat4.translate(modelview,modelview,[-1,2.5,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.3,0.4,0.3]);
currentColor = [0.7,0.4,0.1,1];
cylinder.render();
popMatrix();
// Body
pushMatrix();
mat4.translate(modelview,modelview,[-1,1.0,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[1.3,1,2]);
currentColor = [0.0,0.0,0.9,1];
cube.render();
popMatrix();
// Legs
pushMatrix();
mat4.translate(modelview,modelview,[-1.3,3.1,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.6,0.6,3.0]);
currentColor = [0.0,0.0,1,1];
cylinder.render();
popMatrix();
pushMatrix();
mat4.translate(modelview,modelview,[-0.7,3.1,0]);
mat4.rotate(modelview,modelview,(90)/180*Math.PI,[1,0,0]);
mat4.scale(modelview,modelview,[0.6,0.6,3.0]);
currentColor = [0.0,0.0,1,1];
cylinder.render();
popMatrix();
}
/**
* Push a copy of the current modelview matrix onto the matrix stack.
*/
function pushMatrix() {
matrixStack.push( mat4.clone(modelview) );
}
/**
* Restore the modelview matrix to a value popped from the matrix stack.
*/
function popMatrix() {
modelview = matrixStack.pop();
}
<!-- As is no changes -->
/**
* Create one of the basic objects. The modelData holds the data for
* an IFS using the structure from basic-objects-IFS.js. This function
* creates VBOs to hold the coordinates, normal vectors, and indices
* from the IFS, and it loads the data into those buffers. The function
* creates a new object whose properties are the identifies of the
* VBOs. The new object also has a function, render(), that can be called to
* render the object, using all the data from the buffers. That object
* is returned as the value of the function. (The second parameter,
* xtraTranslate, is there because this program was ported from a Java
* version where cylinders were created in a different position, with
* the base on the xy-plane instead of with their center at the origin.
* The xtraTranslate parameter is a 3-vector that is applied as a
* translation to the rendered object. It is used to move the cylinders
* into the position expected by the code that was ported from Java.)
*/
function createModel(modelData, xtraTranslate) {
var model = {};
model.coordsBuffer = gl.createBuffer();
model.normalBuffer = gl.createBuffer();
model.indexBuffer = gl.createBuffer();
model.count = modelData.indices.length;
if (xtraTranslate)
model.xtraTranslate = xtraTranslate;
else
model.xtraTranslate = null;
gl.bindBuffer(gl.ARRAY_BUFFER, model.coordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, modelData.vertexPositions, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, model.normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, modelData.vertexNormals, gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, model.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, modelData.indices, gl.STATIC_DRAW);
model.render = function() { // This function will render the object.
// Since the buffer from which we are taking the coordinates and normals
// change each time an object is drawn, we have to use gl.vertexAttribPointer
// to specify the location of the data. And to do that, we must first
// bind the buffer that contains the data. Similarly, we have to
// bind this object's index buffer before calling gl.drawElements.
gl.bindBuffer(gl.ARRAY_BUFFER, this.coordsBuffer);
gl.vertexAttribPointer(a_coords_loc, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this.normalBuffer);
gl.vertexAttribPointer(a_normal_loc, 3, gl.FLOAT, false, 0, 0);
gl.uniform4fv(u_material.diffuseColor, currentColor);
if (this.xtraTranslate) {
pushMatrix();
mat4.translate(modelview,modelview,this.xtraTranslate);
}
gl.uniformMatrix4fv(u_modelview, false, modelview );
mat3.normalFromMat4(normalMatrix, modelview);
gl.uniformMatrix3fv(u_normalMatrix, false, normalMatrix);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.drawElements(gl.TRIANGLES, this.count, gl.UNSIGNED_SHORT, 0);
if (this.xtraTranslate) {
popMatrix();
}
}
return model;
}
/* Creates a program for use in the WebGL context gl, and returns the
* identifier for that program. If an error occurs while compiling or
* linking the program, an exception of type String is thrown. The error
* string contains the compilation or linking error. If no error occurs,
* the program identifier is the return value of the function.
* The second and third parameters are the id attributes for <script>
* elementst that contain the source code for the vertex and fragment
* shaders.
*/
function createProgram(gl, vertexShaderID, fragmentShaderID) {
function getTextContent( elementID ) {
// This nested function retrieves the text content of an
// element on the web page. It is used here to get the shader
// source code from the script elements that contain it.
var element = document.getElementById(elementID);
var node = element.firstChild;
var str = "";
while (node) {
if (node.nodeType == 3) // this is a text node
str += node.textContent;
node = node.nextSibling;
}
return str;
}
try {
var vertexShaderSource = getTextContent( vertexShaderID );
var fragmentShaderSource = getTextContent( fragmentShaderID );
}
catch (e) {
throw "Error: Could not get shader source code from script elements.";
}
var vsh = gl.createShader( gl.VERTEX_SHADER );
gl.shaderSource(vsh,vertexShaderSource);
gl.compileShader(vsh);
if ( ! gl.getShaderParameter(vsh, gl.COMPILE_STATUS) ) {
throw "Error in vertex shader: " + gl.getShaderInfoLog(vsh);
}
var fsh = gl.createShader( gl.FRAGMENT_SHADER );
gl.shaderSource(fsh, fragmentShaderSource);
gl.compileShader(fsh);
if ( ! gl.getShaderParameter(fsh, gl.COMPILE_STATUS) ) {
throw "Error in fragment shader: " + gl.getShaderInfoLog(fsh);
}
var prog = gl.createProgram();
gl.attachShader(prog,vsh);
gl.attachShader(prog, fsh);
gl.linkProgram(prog);
if ( ! gl.getProgramParameter( prog, gl.LINK_STATUS) ) {
throw "Link error in program: " + gl.getProgramInfoLog(prog);
}
return prog;
}
/* Initialize the WebGL context. Called from init() */
function initGL() {
var prog = createProgram(gl,"vshader-source","fshader-source");
gl.useProgram(prog);
gl.enable(gl.DEPTH_TEST);
/* Get attribute and uniform locations */
a_coords_loc = gl.getAttribLocation(prog, "a_coords");
a_normal_loc = gl.getAttribLocation(prog, "a_normal");
gl.enableVertexAttribArray(a_coords_loc);
gl.enableVertexAttribArray(a_normal_loc);
u_modelview = gl.getUniformLocation(prog, "modelview");
u_projection = gl.getUniformLocation(prog, "projection");
u_normalMatrix = gl.getUniformLocation(prog, "normalMatrix");
u_material = {
diffuseColor: gl.getUniformLocation(prog, "material.diffuseColor"),
specularColor: gl.getUniformLocation(prog, "material.specularColor"),
emissiveColor: gl.getUniformLocation(prog, "material.emissiveColor"),
specularExponent: gl.getUniformLocation(prog, "material.specularExponent")
};
u_lights = new Array(4);
for (var i = 0; i < 4; i++) {
u_lights[i] = {
enabled: gl.getUniformLocation(prog, "lights[" + i + "].enabled"),
position: gl.getUniformLocation(prog, "lights[" + i + "].position"),
color: gl.getUniformLocation(prog, "lights[" + i + "].color")
};
}
gl.uniform3f( u_material.specularColor, 0.1, 0.1, 0.1 ); // specular properties don't change
gl.uniform1f( u_material.specularExponent, 16 );
gl.uniform3f( u_material.emissiveColor, 0, 0, 0); // default, will be changed temporarily for some objects
for (var i = 1; i < 4; i++) { // set defaults for lights
gl.uniform1i( u_lights[i].enabled, 0 );
gl.uniform4f( u_lights[i].position, 0, 0, 1, 0 );
gl.uniform3f( u_lights[i].color, 1,1,1 );
}
// Lights are set on in the draw() method
} // end initGL()
//--------------------------------- animation framework ------------------------------
var animating = false;
/*
This is where you control the animation by changing positions,
and rotations values as needed.
Trial and error works on the numbers. Graph paper design is more efficient.
*/
function frame() {
if (animating) {
frameNumber += 1;
// Positions of the soccer ball
xval += 0.05;
yval += 0.05;
// Rotation of kick leg
// Not perfect by anymeans but Okay for at least one viewing angle.
kval -= 1.5;
if (xval > 8.5 || yval < -8.5) {
xval= 1.3;
yval= -1.8;
kval = 0;
}
if (kval < -90) {
kval = -90; // Don't overextend the soccer all
}
draw();
requestAnimationFrame(frame);
}
}
function setAnimating(run) {
if (run != animating) {
animating = run;
if (animating)
requestAnimationFrame(frame);
}
}
//-------------------------------------------------------------------------
/**
* initialization function that will be called when the page has loaded
*/
function init() {
try {
var canvas = document.getElementById("webglcanvas");
gl = canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl");
if ( ! gl ) {
throw "Browser does not support WebGL";
}
}
catch (e) {
document.getElementById("message").innerHTML =
"<p>Sorry, could not get a WebGL graphics context.</p>";
return;
}
try {
initGL(); // initialize the WebGL graphics context
}
catch (e) {
document.getElementById("message").innerHTML =
"<p>Sorry, could not initialize the WebGL graphics context:" + e + "</p>";
return;
}
document.getElementById("animCheck").checked = false;
document.getElementById("reset").onclick = function() {
rotator.setView(17,[0,1,2]);
frameNumber = 0;
// Initialize soccer ball positions
xval = 1.5;
yval = -1.5;
animating = false;
document.getElementById("animCheck").checked = false;
draw();
}
// Not really using all of these
// As you create your scene use these or create from primitives
torus = createModel(uvTorus(0.5,1,16,8)); // Create all the basic objects.
sphere = createModel(uvSphere(1));
cone = createModel(uvCone(),[0,0,.5]);
cylinder = createModel(uvCylinder(),[0,0,1.5]);
disk = createModel(uvCylinder(5.5,0.5,64),[0,0,.25]);
ring = createModel(ring(3.3,4.8,40));
cube = createModel(cube());
// This controls the zoom and initial placement
rotator = new TrackballRotator(canvas,function() {
if (!animating)
draw();
},17,[0,1,2]);
draw();
}
</script>
</head>
Hello everyone,
I am trying to render a image using webgl shaders and I have successfully done that using webgl samples but the issue is that when i increase the size of image the quality of image is not good. I want to upscale and interpolate the image using vertex and fragment shader.Here is my sample
"use strict";
function main() {
var image = new Image();
requestCORSIfNotSameOrigin(image, "https://upload.wikimedia.org/wikipedia/commons/5/57/Pneumothorax_CT.jpg")
image.src = "https://upload.wikimedia.org/wikipedia/commons/5/57/Pneumothorax_CT.jpg";
image.width = 1000;
image.height = 1000;
image.onload = function() {
render(image);
}
}
function render(image) {
// Get A WebGL context
/** #type {HTMLCanvasElement} */
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texcoordLocation = gl.getAttribLocation(program, "a_texCoord");
// Create a buffer to put three 2d clip space points in
var positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Set a rectangle the same size as the image.
setRectangle(gl, 0, 0, image.width, image.height);
// provide texture coordinates for the rectangle.
var texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0,
]), gl.STATIC_DRAW);
// Create a texture.
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set the parameters so we can render any size image.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// Upload the image into the texture.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
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 position attribute
gl.enableVertexAttribArray(positionLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset)
// Turn on the teccord attribute
gl.enableVertexAttribArray(texcoordLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
texcoordLocation, size, type, normalize, stride, offset)
// set the resolution
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
// Draw the rectangle.
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2,
]), gl.STATIC_DRAW);
}
main();
// This is needed if the images are not on the same domain
// NOTE: The server providing the images must give CORS permissions
// in order to be able to use the image with WebGL. Most sites
// do NOT give permission.
// See: http://webglfundamentals.org/webgl/lessons/webgl-cors-permission.html
function requestCORSIfNotSameOrigin(img, url) {
if ((new URL(url)).origin !== window.location.origin) {
img.crossOrigin = "";
}
}
#import url("https://webglfundamentals.org/webgl/resources/webgl-tutorials.css");
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<div style="height:700px; width:700px; overflow:scroll;">
<canvas id="canvas"></canvas>
</div>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord; void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
// our texture
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
void main() {
// Look up a color from the texture.
gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
I need interpolation when image zoomed or if set by maximum height like AMI exmaple provided below Check This sample
It's not clear what you want to happen.
First off you set gl.NEAREST as your filtering. WebGL has several kind of filtering covered here. Setting them to gl.LINEAR would be better but only
a little
The problem is WebGL 1.0 doesn't support mips for images that are not power of 2 dimensions (2, 4, 8, 16, 32, 128, 256, 512, 1024, etc...). That page describes what mips are used for (interpolation) but mips can only be used on images that are power of 2 dimensions. The image you're trying to display is not power of 2 dimensions, it's 954 × 687 .
You have a few different options.
Download the image, edit to be power of 2 in both dimensions in a photo editing application. Then call gl.generateMipmap to generate mips for interpolation as described in that page
Copy the image to canvas that's a power of 2 in size then upload the canvas as a texture
Create a texture that's the next largest power of 2 then upload your image
function nearestGreaterOrEqualPowerOf2(v) {
return Math.pow(2, Math.ceil(Math.log2(v)));
}
const newWidth = nearestGreaterOrEqualPowerOf2(image.width);
const newHeight = nearestGreaterOrEqualPowerOf2(image.height);
// first make an empty texture of the new size
const level = 0;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
const border = 0;
gl.texImage2D(gl.TEXTURE_2D, level, format, newWidth, newHeight, border,
format, type, null);
// then upload the image into the bottom left corner of the texture
const xoffset = 0;
const yoffset = 0;
gl.texSubImage2D(gl.TEXTURE_2D, level, xoffset, yoffset, format, type, image);
// now because the texture is a power of 2 in both dimensions you can
// generate mips and turn on maximum filtering
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
You have a new issue though in all these cases which is that the image is now just using a portion of the texture. You'd have to adjust your texture coordinates either using a texture matrix or by adjusting your texture coordinates directly.
// compute needed texture coordinates to show only portion of texture
var u = newWidth / image.width;
var v = newHeight / image.height;
// provide texture coordinates for the rectangle.
var texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
u, 0,
0, v,
0, v,
u, 0,
u, v,
]), gl.STATIC_DRAW);
"use strict";
function main() {
var image = new Image();
requestCORSIfNotSameOrigin(image, "https://upload.wikimedia.org/wikipedia/commons/5/57/Pneumothorax_CT.jpg")
image.src = "https://upload.wikimedia.org/wikipedia/commons/5/57/Pneumothorax_CT.jpg";
image.onload = function() {
render(image);
}
}
function render(image) {
// Get A WebGL context
/** #type {HTMLCanvasElement} */
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
var texcoordLocation = gl.getAttribLocation(program, "a_texCoord");
function nearestGreaterOrEqualPowerOf2(v) {
return Math.pow(2, Math.ceil(Math.log2(v)));
}
const newWidth = nearestGreaterOrEqualPowerOf2(image.width);
const newHeight = nearestGreaterOrEqualPowerOf2(image.height);
// Create a buffer to put three 2d clip space points in
var positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Set a rectangle fit in the canvas at the same aspect as the image.
const drawWidth = canvas.clientWidth;
const drawHeight = canvas.clientWidth / drawWidth * image.height;
setRectangle(gl, 0, 0, drawWidth, drawHeight);
// compute needed texture coordinates to show only portion of texture
var u = newWidth / image.width;
var v = newHeight / image.height;
// provide texture coordinates for the rectangle.
var texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
u, 0,
0, v,
0, v,
u, 0,
u, v,
]), gl.STATIC_DRAW);
// Create a texture.
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// first make an empty texture of the new size
{
const level = 0;
const format = gl.RGBA;
const type = gl.UNSIGNED_BYTE;
const border = 0;
gl.texImage2D(gl.TEXTURE_2D, level, format, newWidth, newHeight, border,
format, type, null);
// then upload the image into the bottom left corner of the texture
const xoffset = 0;
const yoffset = 0;
gl.texSubImage2D(gl.TEXTURE_2D, level, xoffset, yoffset, format, type, image);
}
// now because the texture is a power of 2 in both dimensions you can
// generate mips and turn on maximum filtering
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
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 position attribute
gl.enableVertexAttribArray(positionLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset)
// Turn on the teccord attribute
gl.enableVertexAttribArray(texcoordLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
// Tell the position attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
texcoordLocation, size, type, normalize, stride, offset)
// set the resolution
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
// Draw the rectangle.
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2,
]), gl.STATIC_DRAW);
}
main();
// This is needed if the images are not on the same domain
// NOTE: The server providing the images must give CORS permissions
// in order to be able to use the image with WebGL. Most sites
// do NOT give permission.
// See: http://webglfundamentals.org/webgl/lessons/webgl-cors-permission.html
function requestCORSIfNotSameOrigin(img, url) {
if ((new URL(url)).origin !== window.location.origin) {
img.crossOrigin = "";
}
}
#import url("https://webglfundamentals.org/webgl/resources/webgl-tutorials.css");
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<div style="height:700px; width:700px; overflow:scroll;">
<canvas id="canvas"></canvas>
</div>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord; void main() {
// convert the rectangle from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
// our texture
uniform sampler2D u_image;
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
void main() {
// Look up a color from the texture.
gl_FragColor = texture2D(u_image, v_texCoord);
}
</script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
Already really informative and good answers and comments here.
Also please take into account, that the link you provided using high quality images with high-res and excellent quality, at least with no compression artifacts.
Unpacked ~21mb in NIFTI Data Format.
using ami.js to uppack it:
https://github.com/FNNDSC/ami
Using the exampel by gman with a good image resolution that fits with your screen resolution, should give you a descend result.
Yes, their are some algorithms to fix a bad images quality and deal with image compression artifacts, but (and i don't whant to repeat the comments here) generally speaking once the information is lost, it is gone.
I'm trying to draw a simple quad via webgl. Here is some data:
vertex data: (2 floats per vertex)
0: 0
1: 0
2: 1
3: 0
4: 1
5: 1
6: 0
7: 1
tex coords: (doesnt really matter)
0: -1
1: 1
2: 1
3: 1
4: 1
5: -1
6: -1
7: -1
Indices:
0: 3
1: 0
2: 1
3: 3
4: 1
5: 2
Shaders:
<script id="shader-fs" type="x-shader/x-fragment">
varying highp vec2 vTextureCoord;
uniform highp vec3 uColor;
uniform sampler2D uSampler;
uniform int uSamplerCount;
void main(void) {
highp vec4 texColor =vec4(uColor, 1.0);
if(uSamplerCount > 0)
texColor = texture2D(uSampler, vTextureCoord);
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); //just white for now
}
</script>
<!-- Vertex shader program -->
<script id="shader-vs" type="x-shader/x-vertex">
attribute highp vec2 aVertexPosition;
attribute highp vec2 aTextureCoord;
uniform highp vec2 uPosition;
uniform highp float uZLayer;
varying highp vec2 vTextureCoord;
void main(void) {
gl_Position = vec4(uPosition + aVertexPosition, uZLayer, 1.0);
vTextureCoord = aTextureCoord;
}
</script>
To me this seems all right. Now here I'm binding the buffers:
$p.UpdateBuffers = function ConvexSprite_UpdateBuffers() {
this.gl.bindBuffer(34962 /* WebGLRenderingContext.ARRAY_BUFFER */, this.bVertexPositions);
this.gl.bufferData(34962 /* WebGLRenderingContext.ARRAY_BUFFER */, this.rotatedPoints, 35044 /* WebGLRenderingContext.STATIC_DRAW */);
// string s = rotatedPoints.join(",");
// System.Console.WriteLine("Vertices: " + s);
this.gl.bindBuffer(34962 /* WebGLRenderingContext.ARRAY_BUFFER */, this.bTextureCoords);
this.gl.bufferData(34962 /* WebGLRenderingContext.ARRAY_BUFFER */, this.texCoords, 35044 /* WebGLRenderingContext.STATIC_DRAW */);
//System.Console.WriteLine("Texcoords: " + texCoords.join(","));
this.gl.bindBuffer(34963 /* WebGLRenderingContext.ELEMENT_ARRAY_BUFFER */, this.bIndices);
this.gl.bufferData(34963 /* WebGLRenderingContext.ELEMENT_ARRAY_BUFFER */, this.triangles, 35044 /* WebGLRenderingContext.STATIC_DRAW */);
};
And this is how I'm drawing:
private void DrawScene()
{
gl.viewport(0, 0, (int)canvas.width, (int)canvas.height);
gl.clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);
testSprite.Draw(aVertexPosition, aTextureCoord, uColor, uPosition, uZLayer, uSampler, uSamplerCount);
}
draw function:
public void Draw(uint aVertexPosition, uint aTextureCoord,
WebGLUniformLocation uColor, WebGLUniformLocation uPosition, WebGLUniformLocation uZLayer, WebGLUniformLocation uSampler, WebGLUniformLocation uSamplerCount)
{
//position
gl.uniform2f(uPosition, this.position.x, this.position.y);
gl.uniform1f(uZLayer, this.position.z);
gl.uniform3f(uColor, 1f, 0.5f, 0.5f);
gl.uniform1i(uSamplerCount, 0);
//vertex data
gl.bindBuffer(GL.ARRAY_BUFFER, bVertexPositions);
gl.vertexAttribPointer(aVertexPosition, 2, GL.FLOAT, false, 0, 0);
gl.bindBuffer(GL.ARRAY_BUFFER, bTextureCoords);
gl.vertexAttribPointer(aTextureCoord, 2, GL.FLOAT, false, 0, 0);
gl.bindBuffer(GL.ELEMENT_ARRAY_BUFFER, bIndices);
//texture
// gl.activeTexture(GL.TEXTURE0);
// gl.bindTexture(GL.TEXTURE_2D, texture);
// gl.uniform1i(uSampler, 0);
//draw
gl.drawElements(GL.TRIANGLES, triangles.length, GL.UNSIGNED_SHORT, 0);
}
Unfortunately all I'm getting is a black screen. The triangles are ccw, and in the 0-1 position range. Shouldn't this draw something to the screen ? What am I forgetting here ?
What values are this.position.x and this.position.y? Given you have a 0,1 quad the best you're going to be able to do with this shader and those vertex coordinates is draw a quad on 1/4th of the canvas. The values you need to output have to be in clipspace (-1 <-> +1).
To convert from pixels to clipspace
clipX = pixelX / gl.canvas.width * 2 - 1
clipY = pixelY / gl.canvas.height * 2 - 1
for width and height in clipspace from pixels
clipWidth = pixelWidth / gl.canvas.width * 2
clipHeight = pixelHeight / gl.canvas.height * 2
The first thing I do when debugging a WebGL program that is not drawing is change the fragment shader to the simplest thing possible
gl_FragColor = vec4(1,0,0,1); return;
If I see red where I expect it I know the issue is in the fragcoord. If I don't I know the issue is somewhere else.
Next I might use the WebGL Inspector to see what values I'm passing in the uniforms. For example if this.position.x or this.position.y is greater than 1 or less than -2 then nothing is going to appear because your quad is 0 to 1 so 0 + 1 = 1 which means your quad will be off the right side or top side of the canvas. 1 + -2 = -1 in which case the quad will be off the left or bottom side of the canvas.
Some other things. You set it draw in white gl_FragColor = vec4(1,1,1,1); but the default webpage color is white and the default clear color is 0,0,0,0 (transparent black) which means unless you either (a) made a canvas with no alpha (b) set the clear color somewhere not show or (c) set the CSS for the page or the canvas to a different color you're not going to see anything.
Here's some code that draws quads at pixel locations
"use strict";
window.onload = function() {
// Get A WebGL context
var canvas = document.getElementById("c");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
gl.useProgram(program);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
// look up uniform locations
var u_matrixLoc = gl.getUniformLocation(program, "u_matrix");
var u_colorLoc = gl.getUniformLocation(program, "u_color");
// provide texture coordinates for the rectangle.
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
function rand(min, max) {
return min + Math.random() * (max - min) | 0;
}
for (var ii = 0; ii < 300; ++ii) {
var dstX = rand(0, gl.canvas.width - 20);
var dstY = rand(0, gl.canvas.height - 20);
var dstWidth = rand(10, 30);
var dstHeight = rand(10, 30);
// convert dst pixel coords to clipspace coords
var clipX = dstX / gl.canvas.width * 2 - 1;
var clipY = dstY / gl.canvas.height * -2 + 1;
var clipWidth = dstWidth / gl.canvas.width * 2;
var clipHeight = dstHeight / gl.canvas.height * -2;
// build a matrix that will stretch our
// unit quad to our desired size and location
gl.uniformMatrix3fv(u_matrixLoc, false, [
clipWidth, 0, 0,
0, clipHeight, 0,
clipX, clipY, 1,
]);
gl.uniform4f(u_colorLoc, Math.random(), Math.random(), Math.random(), 1);
// Draw the rectangle.
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
}
canvas {
border: 1px solid black;
}
<script src="//webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<canvas id="c"></canvas>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
uniform mat3 u_matrix;
void main() {
gl_Position = vec4(u_matrix * vec3(a_position, 1), 1);
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
// our texture
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
</script>
If you don't get the matrix math here's an article that explains it.
One other suggestion, instead of a conditional on uSamplerCount you might consider just
gl_FragColor = uColor * texture2D(uSampler, vTextureCoord);
Then make a 1 pixel white texture
whiteTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, whiteTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Float32Array([255,255,255,255]));
Now whenever you want to draw with a solid color then
gl.bindTexture(gl.TEXTURE_2D, whiteTex);
gl.uniform4f(uColorLocation, r, g, b, a);
Whenever you want to draw with a texture
gl.bindTexture(gl.TEXTURE_2D, someTexture);
gl.uniform4f(uColorLocation, 1, 1, 1, 1);