How do I render a 2D Sprite using WebGL? - opengl

I'm struggling to render a 2D Sprite to a Canvas using Dart and WebGL. I can find very few examples of this online; most are either 3D, or contain tons of spaghetti code with no real explanation of what they're doing. I'm trying to do the simplest thing that renders a sprite.
So far, I've managed to render a green square (two triangles) on a canvas. The bit I'm struggling with, is how to change this from a green square to using my texture (the texture is loaded and bound correctly, I believe). I think this will need changes to the shaders (to take texture co-ords, instead of colour) and something to pass texture coords relating to the vertices in the buffer.
This code also exists in a Gist.
Note: This is just a throwaway sample; most of the code lives in the constructor; I'm not too interested in how tidy the code is for now; I can tidy up when I can see a sprite on the screen!
Note: I'm not interested in using a third-party library; I'm doing this to learn WebGL!
<!DOCTYPE html>
<html>
<head>
<title>MySecondGame</title>
</head>
<body>
<canvas width="1024" height="768"></canvas>
<div style="display: none;">
<img id="img-player" src="assets/player.png" />
</div>
<script id="vertex" type="x-shader">
attribute vec2 aVertexPosition;
void main() {
gl_Position = vec4(aVertexPosition, 0.0, 1.0);
}
</script>
<script id="fragment" type="x-shader">
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 uColor;
void main() {
gl_FragColor = uColor;
}
</script>
<script type="application/dart">
import 'dart:async';
import 'dart:html';
import 'dart:math';
import 'dart:typed_data';
import 'dart:web_gl';
Game game;
main() {
game = new Game(document.querySelector('canvas'));
}
class Game {
RenderingContext _gl;
Buffer vbuffer;
int numItems;
Texture playerTexture;
double elapsedTime;
double fadeAmount;
Game(CanvasElement canvas) {
_gl = canvas.getContext3d();
playerTexture = _gl.createTexture();
_gl.bindTexture(TEXTURE_2D, playerTexture);
_gl.texImage2DUntyped(TEXTURE_2D, 0, RGBA, RGBA, UNSIGNED_BYTE, document.querySelector('#img-player'));
_gl.texParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST);
_gl.texParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR_MIPMAP_NEAREST);
_gl.generateMipmap(TEXTURE_2D);
_gl.bindTexture(TEXTURE_2D, null);
var vsScript = document.querySelector('#vertex');
var vs = _gl.createShader(VERTEX_SHADER);
_gl.shaderSource(vs, vsScript.text);
_gl.compileShader(vs);
var fsScript = document.querySelector('#fragment');
var fs = _gl.createShader(FRAGMENT_SHADER);
_gl.shaderSource(fs, fsScript.text);
_gl.compileShader(fs);
var program = _gl.createProgram();
_gl.attachShader(program, vs);
_gl.attachShader(program, fs);
_gl.linkProgram(program);
if (!_gl.getShaderParameter(vs, COMPILE_STATUS))
print(_gl.getShaderInfoLog(vs));
if (!_gl.getShaderParameter(fs, COMPILE_STATUS))
print(_gl.getShaderInfoLog(fs));
if (!_gl.getProgramParameter(program, LINK_STATUS))
print(_gl.getProgramInfoLog(program));
var aspect = canvas.width / canvas.height;
var vertices = new Float32List.fromList([
-0.5, 0.5 * aspect, 0.5, 0.5 * aspect, 0.5, -0.5 * aspect, // Triangle 1
-0.5, 0.5 * aspect, 0.5,-0.5 * aspect, -0.5, -0.5 * aspect // Triangle 2
]);
vbuffer = _gl.createBuffer();
_gl.bindBuffer(ARRAY_BUFFER, vbuffer);
_gl.bufferData(ARRAY_BUFFER, vertices, STATIC_DRAW);
numItems = vertices.length ~/ 2;
_gl.useProgram(program);
var uColor = _gl.getUniformLocation(program, "uColor");
_gl.uniform4fv(uColor, new Float32List.fromList([0.0, 0.3, 0.0, 1.0]));
var aVertexPosition = _gl.getAttribLocation(program, "aVertexPosition");
_gl.enableVertexAttribArray(aVertexPosition);
_gl.vertexAttribPointer(aVertexPosition, 2, FLOAT, false, 0, 0);
window.animationFrame.then(_gameLoop);
}
_gameLoop(num time) {
elapsedTime = time;
_update();
_render();
window.animationFrame.then(_gameLoop);
}
_update() {
// Use sine curve for fading. Sine is -1-1, so tweak to be 0 - 1.
fadeAmount = (sin(elapsedTime/1000) / 2) + 0.5;
}
_render() {
// Set colour for clearing to.
_gl.clearColor(fadeAmount, 1 - fadeAmount, 0.0, 1.0);
// Clear.
_gl.clear(RenderingContext.COLOR_BUFFER_BIT);
_gl.bindTexture(TEXTURE_2D, playerTexture);
_gl.drawArrays(TRIANGLES, 0, numItems);
_gl.bindTexture(TEXTURE_2D, null);
}
}
</script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
(Tagging this with opengl too because I believe the solution is likely the same for WebGL/OpenGL).

Ok, managed to make this work. You can see the full diff in a gist here.
I might be wrong; but it seems that I was expecting to set the data in the buffers while I was setting them up; but I couldn't find any way to say which data was for which buffer. I split the code into some setup code:
vbuffer = _gl.createBuffer();
_gl.bindBuffer(ARRAY_BUFFER, vbuffer);
_gl.bufferData(ARRAY_BUFFER, vertices, STATIC_DRAW);
numItems = vertices.length ~/ 2;
tbuffer = _gl.createBuffer();
_gl.bindBuffer(ARRAY_BUFFER, tbuffer);
_gl.bufferData(ARRAY_BUFFER, textureCoords, STATIC_DRAW);
aVertexPosition = _gl.getAttribLocation(program, "aVertexPosition");
_gl.enableVertexAttribArray(aVertexPosition);
aTextureCoord = _gl.getAttribLocation(program, "aTextureCoord");
_gl.enableVertexAttribArray(aTextureCoord);
uSampler = _gl.getUniformLocation(program, "uSampler");
and some rendering code:
_gl.bindBuffer(ARRAY_BUFFER, vbuffer);
_gl.vertexAttribPointer(aVertexPosition, 2, FLOAT, false, 0, 0);
_gl.bindBuffer(ARRAY_BUFFER, tbuffer);
_gl.vertexAttribPointer(aTextureCoord, 2, FLOAT, false, 0, 0);
_gl.bindTexture(TEXTURE_2D, playerTexture);
_gl.uniform1i(uSampler, 0);
_gl.drawArrays(TRIANGLES, 0, numItems);
I'm not entirely sure if this is correct (it feels like I'm sending the same vertex and textureCoord every frame), but it's working.

Related

Move model in OpenGL by translate matrix instead of glTranslate()

My goal is moving object by arrow keys pressing. For this purpose in callback I create translate matrix from some offset and multiply it to world matrix. However it doesn't work - cube doesn't move by pressing keys. I also noticed using glTranslate() directly with setted offset works good but looks like crutch. I mean, I should use only translation matrices for any transformation of model.
Where is the problem in my code? How to fix it? Why glTranslate() works good?
Minimal code example:
glm::mat4 mWorldMatrix;
glm::mat4 mViewMatrix;
glm::mat4 mProjMatrix;
void onKeyCallback(GLFWwindow*, int key, int scan, int action, int mods)
{
switch (key)
{
case GLFW_KEY_UP:
{
auto translationMatrix = glm::translate(glm::mat4{}, glm::vec3{ 0, 1, 0 });
mWorldMatrix = mWorldMatrix * translationMatrix;
break;
}
case GLFW_KEY_DOWN:
{
auto translationMatrix = glm::translate(glm::mat4{}, glm::vec3{ 0, -1, 0 });
mWorldMatrix = mWorldMatrix * translationMatrix;
break;
}
case GLFW_KEY_LEFT:
{
auto translationMatrix = glm::translate(glm::mat4{}, glm::vec3{ -1, 0, 0 });
mWorldMatrix = mWorldMatrix * translationMatrix;
break;
}
case GLFW_KEY_RIGHT:
{
auto translationMatrix = glm::translate(glm::mat4{}, glm::vec3{ 1, 0, 0 });
mWorldMatrix = mWorldMatrix * translationMatrix;
break;
}
}
}
int main()
{
glfwInit();
const int weight = 640;
const int height = 480;
auto mWindow = glfwCreateWindow(weight, height, "TesT", nullptr, nullptr);
glfwMakeContextCurrent(mWindow);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
mWorldMatrix = glm::mat4{ 1.0f };
mViewMatrix = glm::lookAt(glm::vec3{ 0, 0, -1 },
glm::vec3{ 0, 0, 0 },
glm::vec3{ 0, 1, 0 });
mProjMatrix = glm::perspective(glm::radians(45.0f),
static_cast<float>(weight) / height,
0.1f,
100.0f);
glfwSetKeyCallback(mWindow, onKeyCallback);
while (!glfwWindowShouldClose(mWindow)) {
glClearColor(0.5, 0.5, 0.5, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0,0, weight, height);
auto modelViewMatrix = mViewMatrix * mWorldMatrix;
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(static_cast<const float*>(glm::value_ptr(modelViewMatrix)));
glMatrixMode(GL_PROJECTION_MATRIX);
glLoadMatrixf(static_cast<const float*>(glm::value_ptr(mProjMatrix)));
Cube cube{ glm::vec3{0.5, 0.5, 0.5}, 1 }; //cube with center in {0.5} and side length 1
auto vertices = cube.soup(); //vector of vertex
glTranslatef(0 /* + offset.x*/, 0/* + offset.y*/, -5); //Setting offset here is work good
glBegin(GL_TRIANGLES);
for (const auto& vertex : vertices)
{
glColor3f(vertex.position.x, vertex.position.y, vertex.position.z);
glVertex3f(vertex.position.x, vertex.position.y, vertex.position.z);
}
glEnd();
glfwSwapBuffers(mWindow);
glfwWaitEvents();
}
glfwTerminate();
return 0;
}
Note, that drawing by glBegin/glEnd sequences and the fixed function matrix stack and fixed function. See Fixed Function Pipeline and Legacy OpenGL.
Read about Vertex Specification and Shader for a state of the art way of rendering.
The projection matrix should be applied to the projection matrix stack and the model view matrix to the model view matrix stack.
There are 2 issues.
GL_PROJECTION_MATRIX is not a valid enum constant for glMatrixMode and will cause a GL_INVALID_ENUM error. The valid enum constant for the projection matrix mode is GL_PROJECTION. GL_PROJECTION_MATRIX would be used for reading the current projection matrix by glGetFloatv.
If you want to apply additional transformation to the model, then you have to choose the GL_MODELVIEW matrix before. If the GL_PROJECTION matrix is "selected", this state is kept until it is changed again explicitly.
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(static_cast<const float*>(glm::value_ptr(modelViewMatrix)));
glMatrixMode(GL_PROJECTION); // <----- `GL_PROJECTION` instead of `GL_PROJECTION_MATRIX`
glLoadMatrixf(static_cast<const float*>(glm::value_ptr(mProjMatrix)));
// [...]
glMatrixMode(GL_MODELVIEW); // <-------- specify `GL_MODELVIEW`
glTranslatef(0 /* + offset.x*/, 0/* + offset.y*/, -5); //Setting offset here is work good
Because in every loop you set the cube to the original position. You should define these objects outside the loop. Then you can mutate them in the while loop.
In openGL lessons at the university I had very much use of a youtube series from "The Cherno". He made openGL course.

Shaders: How to draw 3D point verts without generating geometry?

I have a 3D Webgl scene. I am using Regl http://regl.party/ . Which is WebGL. So I am essentially writing straight GLSL.
This is a game project. I have an array of 3D positions [[x,y,z] ...] which are bullets, or projectiles. I want to draw these bullets as a simple cube, sphere, or particle. No requirement on the appearance.
How can I make shaders and a draw call for this without having to create a repeated duplicate set of geometry for the bullets?
Preferring an answer with a vert and frag shader example that demonstrates the expected data input and can be reverse engineered to handle the CPU binding layer
You create an regl command which encapsulates a bunch of data. You can then call it with an object.
Each uniform can take an optional function to supply its value. That function is passed a regl context as the first argument and then the object you passed as the second argument so you can call it multiple times with a different object to draw the same thing (same vertices, same shader) somewhere else.
var regl = createREGL()
const objects = [];
const numObjects = 100;
for (let i = 0; i < numObjects; ++i) {
objects.push({
x: rand(-1, 1),
y: rand(-1, 1),
speed: rand(.5, 1.5),
direction: rand(0, Math.PI * 2),
color: [rand(0, 1), rand(0, 1), rand(0, 1), 1],
});
}
function rand(min, max) {
return Math.random() * (max - min) + min;
}
const starPositions = [[0, 0, 0]];
const starElements = [];
const numPoints = 5;
for (let i = 0; i < numPoints; ++i) {
for (let j = 0; j < 2; ++j) {
const a = (i * 2 + j) / (numPoints * 2) * Math.PI * 2;
const r = 0.5 + j * 0.5;
starPositions.push([
Math.sin(a) * r,
Math.cos(a) * r,
0,
]);
}
starElements.push([
0, 1 + i * 2, 1 + i * 2 + 1,
]);
}
const drawStar = regl({
frag: `
precision mediump float;
uniform vec4 color;
void main () {
gl_FragColor = color;
}`,
vert: `
precision mediump float;
attribute vec3 position;
uniform mat4 mat;
void main() {
gl_Position = mat * vec4(position, 1);
}`,
attributes: {
position: starPositions,
},
elements: starElements,
uniforms: {
mat: (ctx, props) => {
const {viewportWidth, viewportHeight} = ctx;
const {x, y} = props;
const aspect = viewportWidth / viewportHeight;
return [.1 / aspect, 0, 0, 0,
0, .1, 0, 0,
0, 0, 0, 0,
x, y, 0, 1];
},
color: (ctx, props) => props.color,
}
})
regl.frame(function () {
regl.clear({
color: [0, 0, 0, 1]
});
objects.forEach((o) => {
o.direction += rand(-0.1, 0.1);
o.x += Math.cos(o.direction) * o.speed * 0.01;
o.y += Math.sin(o.direction) * o.speed * 0.01;
o.x = (o.x + 3) % 2 - 1;
o.y = (o.y + 3) % 2 - 1;
drawStar(o);
});
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/regl/1.3.11/regl.min.js"></script>
You can draw all of the bullets as point sprites, in which case you just need to provide the position and size of each bullet and draw them as GL_POINTS. Each “point” is rasterized to a square based on the output of your vertex shader (which runs once per point). Your fragment shader is called for each fragment in that square, and can color the fragment however it wants—with a flat color, by sampling a texture, or however else you want.
Or you can provide a single model for all bullets, a separate transform for each bullet, and draw them as instanced GL_TRIANGLES or GL_TRIANGLE_STRIP or whatever. Read about instancing on the OpenGL wiki.
Not a WebGL coder so read with prejudice...
Encode the vertexes in a texture
beware of clamping use texture format that does not clamp to <0.0,+1.0> like GL_LUMINANCE32F_ARB or use vertexes in that range only. To check for clamping use:
GLSL debug prints
Render single rectangle covering whole screen
and use the texture from #1 as input. This will ensure that a fragment shader is called for each pixel of the screen/view exactly once.
Inside fragment shader read the texture and check the distance of a fragment to your vertexes
based on it render your stuff or dicard() fragment... spheres are easy, but boxes and other shapes might be complicated to render based on the distance of vertex especially if they can be arbitrary oriented (which need additional info in the input texture).
To ease up this you can prerender them into some texture and use the distance as texture coordinates ...
This answer of mine is using this technique:
raytrace through 3D mesh
You can sometimes get away with using GL_POINTS with a large gl_PointSize and a customized fragment shader.
An example shown here using distance to point center for fragment alpha. (You could also just as well sample a texture)
The support for large point sizes might be limited though, so check that before deciding on this route.
var canvas = document.getElementById('cvs');
gl = canvas.getContext('webgl');
var vertices = [
-0.5, 0.75,0.0,
0.0, 0.5, 0.0,
-0.75,0.25,0.0,
];
var vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var vertCode =
`attribute vec3 coord;
void main(void) {
gl_Position = vec4(coord, 1.0);
gl_PointSize = 50.0;
}`;
var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, vertCode);
gl.compileShader(vertShader);
var fragCode =
`void main(void) {
mediump float ds = distance(gl_PointCoord.xy, vec2(0.5,0.5))*2.0;
mediump vec4 fg_color=vec4(0.0, 0.0, 0.0,1.0- ds);
gl_FragColor = fg_color;
}`;
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, fragCode);
gl.compileShader(fragShader);
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertShader);
gl.attachShader(shaderProgram, fragShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
var coord = gl.getAttribLocation(shaderProgram, "coord");
gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coord);
gl.viewport(0,0,canvas.width,canvas.height);
gl.drawArrays(gl.POINTS, 0, 3);
<!doctype html>
<html>
<body>
<canvas width = "400" height = "400" id = "cvs"></canvas>
</body>
</html>

How to make multiple lights non-additive in opengl

I'm trying to get a model working which has 8 different light sources. I would like to make it so that a surface reaches its "maximum" value when near one light source, but the way the code works now if multiple lights are near a surface, it just keeps increasing in how lit up it is until it is almost pure-white.
For surfaces, I use:
float white[] = {1,1,1,1};
float black[] = {0,0,0,1};
float medium[] = {.5,.5,.5,.5};
glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,white);
glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,black);
glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,medium);
And for the eight lights:
int glLightConstant = GL_LIGHT0 + i;
float Diffuse[] = {(float)0.01*50 ,(float)0.01*50 ,(float)0.01*50 ,1.0f};
float Specular[] = {(float)0.01*0,(float)0.01*0,(float)0.01*0,1.0f};
float Position[] = {(float)positions[i].x,(float)positions[i].y,(float)positions[i].z,(float)1.0};
glEnable(GL_NORMALIZE);
glEnable(GL_LIGHTING);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,0.0);
glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glEnable(glLightConstant);
if(i == 0) {
float Ambient[] = {(float)0.01*0 ,(float)0.01*0 ,(float)0.01*0 ,1.0f};
glLightfv(glLightConstant,GL_AMBIENT ,Ambient);
}
glLightfv(glLightConstant,GL_DIFFUSE ,Diffuse);
glLightfv(glLightConstant,GL_SPECULAR,Specular);
glLightfv(glLightConstant,GL_POSITION,Position);
I realize that the ambient light is 0, I might change it later.

glVertexAttrib4fv won't pass to shader input at location 0

I'm trying to learn OpenGL and Rust at the same time. I'm using the OpenGL Superbible Sixth Edition, and got stuck in chapter 3 which introduces the function glVertexAttrib4fv to offset the position of a triangle. It worked fine when I did it in C++, but when I tried to translate it to Rust, the triangle disappeared. I've tried to reduce the example as much as possible to the following code (cargo dependencies are glutin = "*" and gl = "*"):
main.rs
extern crate glutin;
extern crate gl;
use std::io::Read;
fn main() {
unsafe {
let win = glutin::Window::new().unwrap();
win.make_current().unwrap();
gl::load_with(|s| win.get_proc_address(s));
let program = build_shader_program();
gl::UseProgram(program);
let mut vao = std::mem::uninitialized();
gl::GenVertexArrays(1, &mut vao);
gl::BindVertexArray(vao);
let red = [1.0, 0.0, 0.0, 1.0];
let mut running = true;
while running {
for event in win.poll_events() {
if let glutin::Event::Closed = event {
running = false;
}
}
win.swap_buffers().unwrap();
gl::ClearBufferfv(gl::COLOR, 0, &red[0]);
let attrib = [0.5, 0.0, 0.0, 0.0];
panic_if_error("before VertexAttrib4fv");
gl::VertexAttrib4fv(0, &attrib[0]);
panic_if_error("after VertexAttrib4fv");
gl::DrawArrays(gl::TRIANGLES, 0, 3);
}
}
}
fn panic_if_error(message: &str) {
unsafe {
match gl::GetError() {
gl::NO_ERROR => (),
_ => panic!("{}", message),
}
}
}
fn load_file_as_cstring(path: &str) -> std::ffi::CString {
let mut contents = Vec::new();
let mut file = std::fs::File::open(path).unwrap();
file.read_to_end(&mut contents).unwrap();
std::ffi::CString::new(contents).unwrap()
}
fn load_and_compile_shader(path: &str, shader_type: u32) -> u32 {
let contents = load_file_as_cstring(path);
unsafe {
let shader_id = gl::CreateShader(shader_type);
let source_ptr = contents.as_ptr();
gl::ShaderSource(shader_id, 1, &source_ptr, std::ptr::null());
gl::CompileShader(shader_id);
let mut result = std::mem::uninitialized();
gl::GetShaderiv(shader_id, gl::COMPILE_STATUS, &mut result);
assert_eq!(result, gl::TRUE as i32);
shader_id
}
}
fn build_shader_program() -> u32 {
let vert = load_and_compile_shader("a.vert", gl::VERTEX_SHADER);
let frag = load_and_compile_shader("a.frag", gl::FRAGMENT_SHADER);
unsafe {
let program_id = gl::CreateProgram();
gl::AttachShader(program_id, vert);
gl::AttachShader(program_id, frag);
gl::LinkProgram(program_id);
let mut result = std::mem::uninitialized();
gl::GetProgramiv(program_id, gl::LINK_STATUS, &mut result);
assert_eq!(result, gl::TRUE as i32);
program_id
}
}
a.frag
#version 430 core
out vec4 color;
void main() {
color = vec4(1.0, 1.0, 1.0, 1.0);
}
a.vert
#version 430 core
layout (location = 0) in vec4 offset;
void main() {
const vec4 vertices[3] =
vec4[3](vec4( 0.25, -0.25, 0.5, 1.0),
vec4(-0.25, -0.25, 0.5, 1.0),
vec4( 0.25, 0.25, 0.5, 1.0));
gl_Position = vertices[gl_VertexID]; // LINE 1
// gl_Position = vertices[gl_VertexID] + offset; // LINE 2
}
This code, as is, produces a white triangle in the middle of a red window.
Now, my expectation is that when I comment out LINE 1 in the vertex shader, and uncomment LINE 2, the triangle should move a quarter of a screen to the right, due to this code in "main.rs":
let attrib = [0.5, 0.0, 0.0, 0.0];
panic_if_error("before VertexAttrib4fv");
gl::VertexAttrib4fv(0, &attrib[0]);
panic_if_error("after VertexAttrib4fv");
But instead, the triangle disappears altogether. The panic_if_error call before and after gl::VertexAttrib4fv ensures that gl::GetError returns gl::NO_ERROR.
Question: Does anybody know why this is happening?
Other things of note. While I was searching for the answer to this, I came upon this question, where the user is having a similar problem (except in C++, where I had no problem). Anyway, one of the comments there incidentally lead me to try changing the location from 0 to 1, as in this:
layout (location = 1) in vec4 offset;
for the vertex shader, and this for the call to gl::VertexAttrib4fv:
gl::VertexAttrib4fv(1, &attrib[0]);
Well, that worked, but I have no idea why, and would still like to know what the problem is with using location 0 there (since that's what the book shows, and it worked fine in C++).
You need to make sure that you have a Core Profile context. If you do not specify this, you may be creating a Compatibility Profile context. In the Compatibility Profile, vertex attribute 0 has a special meaning. From the OpenGL 3.2 Compatibility Profile spec:
Setting generic vertex attribute zero specifies a vertex; the four vertex coordinates are taken from the values of attribute zero. A Vertex2, Vertex3, or Vertex4 command is completely equivalent to the corresponding VertexAttrib* command with an index of zero. Setting any other generic vertex attribute updates the current values of the attribute. There are no current values for vertex attribute zero.
In other words, vertex attribute 0 is an alias for the fixed function vertex position in the compatibility profile.
The above does not apply in the Core Profile. Vertex attribute 0 has not special meaning, and can be used like any other vertex attribute.
Based on what you already found, you need to use the with_gl_profile method with argument Core to specify that you want to use the core profile when creating the window.

Go Lang OpenGL simple shape - blank screen

I need some assistance into why this piece of code produces a blank green window. I made this by combining examples from https://github.com/Jragonmiris/mathgl/blob/master/examples/opengl-tutorial/tutorial02/main.go and https://github.com/veandco/go-sdl2/blob/master/examples/opengl3.go. I guess i'm not sure if this is a bug with the GoLang sdl/gl framework or an issue with my OpenGL understanding. All this should draw is a cube.
My code is:
package main
import (
"fmt"
// gl "github.com/chsc/gogl/gl33"
"github.com/veandco/go-sdl2/sdl"
// "math"
"github.com/Jragonmiris/mathgl"
"github.com/go-gl/gl"
"runtime"
"time"
)
// var program gl.Program = 0
// var buffer gl.Buffer = 0
func MakeProgram(vert, frag string) gl.Program {
vertShader, fragShader := gl.CreateShader(gl.VERTEX_SHADER), gl.CreateShader(gl.FRAGMENT_SHADER)
vertShader.Source(vert)
fragShader.Source(frag)
vertShader.Compile()
fragShader.Compile()
prog := gl.CreateProgram()
prog.AttachShader(vertShader)
prog.AttachShader(fragShader)
prog.Link()
prog.Validate()
fmt.Println(prog.GetInfoLog())
return prog
}
func main() {
var window *sdl.Window
var context sdl.GLContext
var event sdl.Event
var running bool
var err error
runtime.LockOSThread()
if 0 != sdl.Init(sdl.INIT_EVERYTHING) {
panic(sdl.GetError())
}
window, err = sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED,
sdl.WINDOWPOS_UNDEFINED,
winWidth, winHeight, sdl.WINDOW_OPENGL)
if err != nil {
panic(err)
}
if window == nil {
panic(sdl.GetError())
}
context = sdl.GL_CreateContext(window)
if context == nil {
panic(sdl.GetError())
}
if gl.Init() != 0 {
panic("gl error")
}
gl.ClearColor(1.0, 1.0, 1.0, .5)
gl.Viewport(0, 0, winWidth, winHeight)
program := MakeProgram(vertexShaderSource, fragmentShaderSource)
defer program.Delete()
matrixID := program.GetUniformLocation("MVP")
Projection := mathgl.Perspective(45.0, 4.0/3.0, 0.1, 100.0)
View := mathgl.LookAt(4.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
Model := mathgl.Ident4f()
MVP := Projection.Mul4(View).Mul4(Model)
gl.Enable(gl.DEPTH_TEST)
gl.DepthFunc(gl.LESS)
gl.Enable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
vertexArray := gl.GenVertexArray()
defer vertexArray.Delete()
vertexArray.Bind()
buffer := gl.GenBuffer()
defer buffer.Delete()
buffer.Bind(gl.ARRAY_BUFFER)
gl.BufferData(gl.ARRAY_BUFFER, len(triangle_vertices)*4, &triangle_vertices, gl.STATIC_DRAW)
running = true
for running {
for event = sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch t := event.(type) {
case *sdl.QuitEvent:
running = false
case *sdl.MouseMotionEvent:
fmt.Printf(string(t.Timestamp))
}
}
gl.Clear(gl.COLOR_BUFFER_BIT) // | gl.DEPTH_BUFFER_BIT)
program.Use()
matrixID.UniformMatrix4fv(false, MVP)
attribLoc := gl.AttribLocation(0)
attribLoc.EnableArray()
buffer.Bind(gl.ARRAY_BUFFER)
attribLoc.AttribPointer(3, gl.FLOAT, false, 0, nil)
gl.DrawArrays(gl.TRIANGLES, 0, 3)
attribLoc.DisableArray()
time.Sleep(50 * time.Millisecond)
sdl.GL_SwapWindow(window)
}
sdl.GL_DeleteContext(context)
window.Destroy()
sdl.Quit()
}
const (
winTitle = "OpenGL Shader"
winWidth = 640
winHeight = 480
vertexShaderSource = `
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;
void main(){
gl_Position = MVP * vec4 (vertexPosition_modelspace,1.0);
}
`
fragmentShaderSource = `
#version 330 core
// Ouput data
out vec3 color;
void main()
{
// Output color = red
color = vec3(1,0,0);
}
`
)
var triangle_vertices = []float32{
-.5, -.5, -.5,
.5, -.5, -.5,
0.0, 0.5, -.5,
}
So I'm still having trouble drawing a simple shape on the screen. I made a few changes such as simplifying my shape (a triangle). I created coordinates so they would be more towards the -z axis so I would be able to see them but that has not worked. I then set the MVP matrix (moving the camera back some) just to make sure. My shaders are simple as I am only passing in a vec3 vertex position and mat4 MVP matrix so believe shaders are working correctly? Sorry for all the confusion, i think i maybe missing something here.
Update:
I also ran the version commands for opengl:
fmt.Println(gl.GetString(gl.VERSION))
fmt.Println(gl.GetString(gl.VENDOR))
fmt.Println(gl.GetString(gl.RENDERER))
for which the output was:
4.5.0 NVIDIA 347.09
NVIDIA Corporation
GeForce GTX 650 Ti/PCIe/SSE2
Not sure if this has any impact?
Update:
I have looked at some more examples and decided to try and add some sdl attributes but still no luck:
sdl.GL_SetAttribute(sdl.GL_DOUBLEBUFFER, 1)
sdl.GL_SetAttribute(sdl.GL_RED_SIZE, 8)
sdl.GL_SetAttribute(sdl.GL_GREEN_SIZE, 8)
sdl.GL_SetAttribute(sdl.GL_BLUE_SIZE, 8)
sdl.GL_SetAttribute(sdl.GL_ALPHA_SIZE, 8)
Update:
I modified this post to just include more recent code to not scare people away from TLDR.
I finally figured out what my problem was in this code.
The first thing I had to do was
positionAttrib := program.GetAttribLocation("vertexPosition_modelspace")
for all the input variables going into the vertex shader. This was done after binding the VBO for each array.
Next,
If you notice my code above:
gl.BufferData(gl.ARRAY_BUFFER, len(triangle_vertices)*4, &triangle_vertices, gl.STATIC_DRAW)
I simply replaced it with triangle_vertices array, and not the address:
gl.BufferData(gl.ARRAY_BUFFER, len(triangle_vertices)*4, triangle_vertices, gl.STATIC_DRAW)
Doing this seemed to fix it.
I would post this as a comment, but I do not yet have enough reputation.
The solution already provided nearly solved my similar issue, however not quite.
Where the provided solution was
gl.BufferData(gl.ARRAY_BUFFER, len(triangle_vertices)*4, triangle_vertices, gl.STATIC_DRAW)
The actual code which solved my issue is
gl.BufferData(gl.ARRAY_BUFFER, len(triangle_vertices)*4, gl.Ptr(triangle_vertices), gl.STATIC_DRAW)
I have also answered a similar question with this, but in more detail, which can be found here:
OpenGL Vertex Buffer doesn't draw anything in golang