I am working on a simple opengl game to learn more about it. But for some reason when I try to rotate my cube over time it becomes stretched. You can see it in the photo:
I think it has to do with my model matrix but I'm not sure. Here is a portion of my code:
model := mgl32.Ident4()
model = model.Mul4(mgl32.HomogRotate3D(float32(glfw.GetTime()) *
mgl32.DegToRad(50.0), mgl32.Vec3{0.5, 1.0, 0.0}))
view := mgl32.Ident4()
view = view.Mul4(mgl32.Translate3D(0.0, 0.0, -3.0))
projection := mgl32.Ident4()
projection = mgl32.Perspective(mgl32.DegToRad(45.0), 800/ float32(600), 0.1, 100)
shader.setMat4("model", model)
shader.setMat4("view", view)
shader.setMat4("projection", projection)
Here is my Minimal, Complete, and Verifiable example. I've removed the texture as it's not necessary to see the problem. I am also using wireframe so that you can see the stretching more clearly.
package main
import(
"github.com/go-gl/glfw/v3.2/glfw"
"github.com/go-gl/gl/v4.5-core/gl"
"log"
"strings"
"fmt"
"github.com/go-gl/mathgl/mgl32"
)
const(
vertexShaderSource = `
#version 450 core
layout (location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
}` + "\x00"
fragmentShaderSource = `
#version 450 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}` + "\x00"
)
var(
vertices = []float32 {
-0.5,0.5,-0.5,
-0.5,-0.5,-0.5,
0.5,-0.5,-0.5,
0.5,0.5,-0.5,
-0.5,0.5,0.5,
-0.5,-0.5,0.5,
0.5,-0.5,0.5,
0.5,0.5,0.5,
0.5,0.5,-0.5,
0.5,-0.5,-0.5,
-0.5,-0.5,0.5,
-0.5,0.5,0.5,
-0.5,0.5,-0.5,
-0.5,-0.5,0.5,
0.5,-0.5,0.5,
}
indices = []int {
0,1,3, // -z
3,1,2,
5,4,7, // +z
7,6,5,
9,8,7, // +x
7,6,9,
0,1,11, // -x
11,1,10,
12,4,7, // +y
7,3,12,
13,1,14, // -y
14,1,2,
}
)
func main() {
if err := glfw.Init(); err != nil {
log.Fatalln("failed to initialize glfw:", err)
}
defer glfw.Terminate()
glfw.WindowHint(glfw.ContextVersionMajor, 4)
glfw.WindowHint(glfw.ContextVersionMinor, 5)
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
window, err := glfw.CreateWindow(800, 600, "LearnOpenGL", nil, nil)
if err != nil {
panic(err)
}
defer window.Destroy()
window.MakeContextCurrent()
// Initialize Glow
if err := gl.Init(); err != nil {
panic(err)
}
gl.Viewport(0, 0, 800, 600)
//SHADERS
vertexID, err := loadShader(vertexShaderSource, gl.VERTEX_SHADER)
if err != nil {
panic(err)
}
fragmentID, err := loadShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
if err != nil {
panic(err)
}
programID := gl.CreateProgram()
gl.AttachShader(programID, vertexID)
gl.AttachShader(programID, fragmentID)
gl.DeleteShader(vertexID)
gl.DeleteShader(fragmentID)
gl.LinkProgram(programID)
// VBO / VAO / EBO data
var vbo, vao, ebo uint32
gl.GenVertexArrays(1, &vao)
gl.GenBuffers(1, &vbo)
gl.GenBuffers(1, &ebo)
gl.BindVertexArray(vao)
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
gl.BufferData(gl.ARRAY_BUFFER, len(vertices)*4, gl.Ptr(vertices), gl.STATIC_DRAW)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(indices) * 4, gl.Ptr(indices), gl.STATIC_DRAW)
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, gl.PtrOffset(0))
gl.EnableVertexAttribArray(0)
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
gl.BindVertexArray(0)
gl.Enable(gl.DEPTH_TEST)
gl.PolygonMode(gl.FRONT_AND_BACK, gl.LINE)
for !window.ShouldClose() {
gl.ClearColor(0.2, 0.3, 0.3, 1.0)
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.UseProgram(programID)
model := mgl32.Ident4()
view := mgl32.Ident4()
projection := mgl32.Ident4()
model = model.Mul4(mgl32.HomogRotate3D(float32(glfw.GetTime()) * mgl32.DegToRad(50), mgl32.Vec3{0.5, 1, 0}))
view = view.Mul4(mgl32.Translate3D(0, 0, -3))
projection = projection.Mul4(mgl32.Perspective(mgl32.DegToRad(45), 800/float32(600), 0.1, 100))
gl.UniformMatrix4fv(gl.GetUniformLocation(programID, gl.Str("model\x00")), 1, false, &model[0])
gl.UniformMatrix4fv(gl.GetUniformLocation(programID, gl.Str("view\x00")), 1, false, &view[0])
gl.UniformMatrix4fv(gl.GetUniformLocation(programID, gl.Str("projection\x00")), 1, false, &projection[0])
gl.BindVertexArray(vao)
gl.DrawElements(gl.TRIANGLES, 36, gl.UNSIGNED_INT, gl.PtrOffset(0))
window.SwapBuffers()
glfw.PollEvents()
}
}
func loadShader(file string, shaderType uint32) (uint32, error) {
shader := gl.CreateShader(shaderType)
csources, free := gl.Strs(string(file))
gl.ShaderSource(shader, 1, csources, nil)
free()
gl.CompileShader(shader)
var status int32
gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
if status == gl.FALSE {
var logLength int32
gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
log := strings.Repeat("\x00", int(logLength+1))
gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
return 0,fmt.Errorf("failed to compile %v: %v", file, log)
}
return shader, nil
}
HomogRotate3D creates a 3D rotation Matrix that rotates by (radian) angle about some arbitrary axis given by a Normalized Vector.
mgl32.Vec3{1, 1, 0}.Normalize()
Is apparently all you needed is normalize the axis of rotation vector and it stops the distortion. So your model rotation line:
model = model.Mul4(mgl32.HomogRotate3D(float32(glfw.GetTime()) *
mgl32.DegToRad(50.0), mgl32.Vec3{1.0, 1.0, 0.0}.Normalize()))
Have fun!
Related
Using WebGL + GLSL, I'm trying to render a prism with the following vertex indices:
So far, though, I haven't been able to get this to render a prism -- I get instead a triangular plane:
var canvas,
gl,
fs,
vs,
glProgram,
vertexBuffer,
vertexIndexBuffer,
colorBuffer,
positionVal,
colorVal,
mvMatrix = mat4.create(),
pMatrix = mat4.create(),
angle = 0.01;
function initWebgl() {
canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
try {
gl = canvas.getContext('webgl')
gl.enable(gl.DEPTH_TEST)
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT)
} catch(err) {
alert('Your browser does not support Webgl')
return;
}
// set the default background color
gl.clearColor(0.9, 0.9, 0.9, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
}
function initCamera() {
// set camera area, fov, near clip, far clip, and translation
gl.viewport(0, 0, canvas.width, canvas.height)
mat4.perspective(45, canvas.width/canvas.height, 0.1, 100, pMatrix);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [0, 0, -2.0]);
}
function initShaders() {
vs = buildShader('#shader-vs', gl.VERTEX_SHADER)
fs = buildShader('#shader-fs', gl.FRAGMENT_SHADER)
}
function buildShader(selector, type) {
var src = document.querySelector(selector).innerHTML;
var shader = gl.createShader(type)
gl.shaderSource(shader, src)
gl.compileShader(shader)
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.warn('Shader error', selector, gl.getShaderInfoLog(shader))
}
return shader;
}
function initProgram() {
glProgram = gl.createProgram()
gl.attachShader(glProgram, vs)
gl.attachShader(glProgram, fs)
gl.linkProgram(glProgram)
if (!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) {
console.warn('Program link error')
}
gl.useProgram(glProgram)
}
function updatePositions() {
mat4.identity(mvMatrix)
mat4.translate(mvMatrix, [-1.0, -1.0, -7.0])
mat4.rotate(mvMatrix, angle, [0.0, 1.0, 0.0])
angle += 0.01;
}
function getBuffers() {
// vertex buffer
var vertexData = new Float32Array([
// front
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0, 0.5, 0.0,
// back
-0.5, -0.5, 5,
0.5, -0.5, 5,
0, 0.5, 5,
])
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW)
// vertex index buffer - creates prism
var vertexIndices = new Uint16Array([
// front
0, 1, 2,
// right
1, 2, 4,
2, 4, 5,
// back
3, 4, 5,
// left
2, 3, 5,
0, 2, 3,
// bottom
0, 1, 3,
1, 3, 4,
])
vertexIndexBuffer = gl.createBuffer()
vertexIndexBuffer.number_vertex_points = vertexIndices.length;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, vertexIndices, gl.STATIC_DRAW)
// color buffer
colorVal = colorVal || 0.5;
colorVal += 0.01;
var colorData = new Float32Array([
Math.sin(colorVal) + 1, Math.cos(colorVal) + 1, 1,
1, Math.sin(colorVal) + 1, 0,
Math.cos(colorVal) + 1, 1, 0,
Math.sin(colorVal) + 1, Math.cos(colorVal) + 1, 1,
1, Math.sin(colorVal) + 1, 0,
Math.cos(colorVal) + 1, 1, 0,
])
colorBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
gl.bufferData(gl.ARRAY_BUFFER, colorData, gl.DYNAMIC_DRAW)
}
function drawBuffers() {
// identify and bind vertex position attributes
var aVertexPosition = gl.getAttribLocation(glProgram, 'aVertexPosition')
gl.enableVertexAttribArray(aVertexPosition)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer)
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0.0, 0.0)
// identify and bind vertex color attributes
var aVertexColor = gl.getAttribLocation(glProgram, 'aVertexColor')
gl.enableVertexAttribArray(aVertexColor)
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
gl.vertexAttribPointer(aVertexColor, 3, gl.FLOAT, false, 0.0, 0.0)
// draw the data
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer)
gl.drawElements(gl.TRIANGLES, vertexIndexBuffer.number_vertex_points,
gl.UNSIGNED_SHORT, 0)
}
function getMatrixUniforms() {
glProgram.pMatrixUniform = gl.getUniformLocation(glProgram, 'uPMatrix')
glProgram.mvMatrixUniform = gl.getUniformLocation(glProgram, 'uMVMatrix')
}
function setMatrixUniforms() {
gl.uniformMatrix4fv(glProgram.pMatrixUniform, false, pMatrix)
gl.uniformMatrix4fv(glProgram.mvMatrixUniform, false, mvMatrix)
}
function render() {
updatePositions()
getBuffers()
drawBuffers()
setMatrixUniforms()
requestAnimationFrame(render, canvas)
}
initWebgl()
initCamera()
initShaders()
initProgram()
getMatrixUniforms()
render()
* {
margin: 0;
padding: 0;
}
body, html {
height: 100%;
width: 100%;
overflow: hidden;
background: skyblue;
}
<script src="https://rawgit.com/duhaime/955402641534b89babd41c8de8bc91f6/raw/5d86d54f7237f4cf2b206dcf0a3d453ba95acd1d/gl-matrix.js"></script>
<script id='shader-vs' type='x-shader/x-vertex'>
attribute vec3 aVertexPosition;
attribute vec3 aVertexColor;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying highp vec4 vColor;
void main() {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = vec4(aVertexColor, 1.0);
}
</script>
<script id='shader-fs' type='x-shader/x-fragment'>
varying highp vec4 vColor;
void main() {
gl_FragColor = vColor;
}
</script>
<canvas />
Does anyone know what I can do to make the prism render? I'd be grateful for any pointers others can offer!
Whoops, I was passing the vertexIndexBuffer to the shaders to specify the positional attribute, but I should have passed the vertexBuffer to specify the positional attribute. This was the intended result:
var canvas,
gl,
fs,
vs,
glProgram,
vertexBuffer,
vertexIndexBuffer,
colorBuffer,
positionVal,
colorVal,
mvMatrix = mat4.create(),
pMatrix = mat4.create(),
angle = 0.01;
function initWebgl() {
canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
try {
gl = canvas.getContext('webgl')
gl.enable(gl.DEPTH_TEST)
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT)
} catch(err) {
alert('Your browser does not support Webgl')
return;
}
// set the default background color
gl.clearColor(0.9, 0.9, 0.9, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
}
function initCamera() {
// set camera area, fov, near clip, far clip, and translation
gl.viewport(0, 0, canvas.width, canvas.height)
mat4.perspective(45, canvas.width/canvas.height, 0.1, 100, pMatrix);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [0, 0, -2.0]);
}
function initShaders() {
vs = buildShader('#shader-vs', gl.VERTEX_SHADER)
fs = buildShader('#shader-fs', gl.FRAGMENT_SHADER)
}
function buildShader(selector, type) {
var src = document.querySelector(selector).innerHTML;
var shader = gl.createShader(type)
gl.shaderSource(shader, src)
gl.compileShader(shader)
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.warn('Shader error', selector, gl.getShaderInfoLog(shader))
}
return shader;
}
function initProgram() {
glProgram = gl.createProgram()
gl.attachShader(glProgram, vs)
gl.attachShader(glProgram, fs)
gl.linkProgram(glProgram)
if (!gl.getProgramParameter(glProgram, gl.LINK_STATUS)) {
console.warn('Program link error')
}
gl.useProgram(glProgram)
}
function updatePositions() {
mat4.identity(mvMatrix)
mat4.translate(mvMatrix, [-1.0, -1.0, -7.0])
mat4.rotate(mvMatrix, angle, [0.0, 1.0, 0.0])
angle += 0.01;
}
function getBuffers() {
// vertex buffer
var vertexData = new Float32Array([
// front
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0,
0, 0.5, 0.0,
// back
-0.5, -0.5, 5,
0.5, -0.5, 5,
0, 0.5, 5,
])
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW)
// vertex index buffer - creates prism
var vertexIndices = new Uint16Array([
// front
0, 1, 2,
// right
1, 2, 4,
2, 4, 5,
// back
3, 4, 5,
// left
2, 3, 5,
0, 2, 3,
// bottom
0, 1, 3,
1, 3, 4,
])
vertexIndexBuffer = gl.createBuffer()
vertexIndexBuffer.number_vertex_points = vertexIndices.length;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, vertexIndices, gl.STATIC_DRAW)
// color buffer
colorVal = colorVal || 0.5;
colorVal += 0.01;
var colorData = new Float32Array([
Math.sin(colorVal) + 1, Math.cos(colorVal) + 1, 1,
1, Math.sin(colorVal) + 1, 0,
Math.cos(colorVal) + 1, 1, 0,
Math.sin(colorVal) + 1, Math.cos(colorVal) + 1, 1,
1, Math.sin(colorVal) + 1, 0,
Math.cos(colorVal) + 1, 1, 0,
])
colorBuffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
gl.bufferData(gl.ARRAY_BUFFER, colorData, gl.DYNAMIC_DRAW)
}
function drawBuffers() {
// identify and bind vertex position attributes
var aVertexPosition = gl.getAttribLocation(glProgram, 'aVertexPosition')
gl.enableVertexAttribArray(aVertexPosition)
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.vertexAttribPointer(aVertexPosition, 3, gl.FLOAT, false, 0.0, 0.0)
// identify and bind vertex color attributes
var aVertexColor = gl.getAttribLocation(glProgram, 'aVertexColor')
gl.enableVertexAttribArray(aVertexColor)
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer)
gl.vertexAttribPointer(aVertexColor, 3, gl.FLOAT, false, 0.0, 0.0)
// draw the data
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer)
gl.drawElements(gl.TRIANGLES, vertexIndexBuffer.number_vertex_points,
gl.UNSIGNED_SHORT, 0)
}
function getMatrixUniforms() {
glProgram.pMatrixUniform = gl.getUniformLocation(glProgram, 'uPMatrix')
glProgram.mvMatrixUniform = gl.getUniformLocation(glProgram, 'uMVMatrix')
}
function setMatrixUniforms() {
gl.uniformMatrix4fv(glProgram.pMatrixUniform, false, pMatrix)
gl.uniformMatrix4fv(glProgram.mvMatrixUniform, false, mvMatrix)
}
function render() {
updatePositions()
getBuffers()
drawBuffers()
setMatrixUniforms()
requestAnimationFrame(render, canvas)
}
initWebgl()
initCamera()
initShaders()
initProgram()
getMatrixUniforms()
render()
* {
margin: 0;
padding: 0;
}
body, html {
height: 100%;
width: 100%;
overflow: hidden;
background: skyblue;
}
<script src="https://rawgit.com/duhaime/955402641534b89babd41c8de8bc91f6/raw/5d86d54f7237f4cf2b206dcf0a3d453ba95acd1d/gl-matrix.js"></script>
<script id='shader-vs' type='x-shader/x-vertex'>
attribute vec3 aVertexPosition;
attribute vec3 aVertexColor;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
varying highp vec4 vColor;
void main() {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vColor = vec4(aVertexColor, 1.0);
}
</script>
<script id='shader-fs' type='x-shader/x-fragment'>
varying highp vec4 vColor;
void main() {
gl_FragColor = vColor;
}
</script>
<canvas />
With WebGL 2 we now can play with Uniform Buffer Objects.
They look like a great idea, not having to attach common uniforms to every single program (like projection and view matrices that are common to every object being rendered).
I created an helper class which I call every time I want to bind a uniform buffer object.
class UniformBuffer {
constructor(gl, data, boundLocation = 0) {
this.boundLocation = boundLocation;
this.data = new Float32Array(data);
this.buffer = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
update(gl, data, offset = 0) {
this.data.set(data, offset);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
};
The idea if to create the uniform buffers like this
const perScene = new UniformBuffer(gl, [
...vec4.create(),
...vec4.create(),
], 0); // and bind it to bind location 0?
const perObject = new UniformBuffer(gl, [
...vec4.create(),
], 1); // and bind it to bind location 1?
In my render loop, I then update the "perScene" uniforms by calling
perScene.update(gl, [
...vec4.fromValues(1, 0, 0, 1),
], 4); // giving an offset to update only the 2nd color.
Then I'll look through all the objects in the scene and my idea is to update the perObject uniform buffer like this
for (let i = 0; i < objects.length; i++) {
perObject.update(gl, [
...vec4.fromValues(0, 0, 1, 1),
]);
}
I'm talking about vec4 just to make the example easier, but the idea is to have matrices (projection and view) on the perScene, and (object and normal matrices) on the perObject.
In my shader I have them declared as
uniform perScene {
vec4 color1;
vec4 color2;
};
uniform perModel {
vec4 color3;
};
I have a working snippet here
class UniformBuffer {
constructor(gl, data, boundLocation = 0) {
this.boundLocation = boundLocation;
this.data = new Float32Array(data);
this.buffer = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
update(gl, data, offset = 0) {
this.data.set(data, offset);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
};
const vertex = `#version 300 es
uniform perScene {
vec4 color1;
vec4 color2;
};
uniform perModel {
vec4 color3;
};
in vec3 a_position;
out vec3 v_color;
void main() {
gl_Position = vec4(a_position, 1.0);
v_color = color1.rgb + color2.rgb; // WORKS
// v_color = color1.rgb + color2.rgb + color3.rgb; // DOESNT WORK
}
`;
const fragment = `#version 300 es
precision highp float;
precision highp int;
in vec3 v_color;
out vec4 outColor;
void main() {
outColor = vec4(v_color, 1.0);
}
`;
const geometry = {
positions: [-0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0],
indices: [0, 2, 1, 1, 2, 3],
};
const renderList = [];
// STEP 1 (create canvas)
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl2");
if (!gl) {
console.log('no webgl2 buddy');
}
// STEP 2 (create program)
const v = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(v, vertex);
gl.compileShader(v);
const f = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(f, fragment);
gl.compileShader(f);
const program = gl.createProgram();
gl.attachShader(program, v);
gl.attachShader(program, f);
gl.linkProgram(program);
// STEP 3 (create VAO)
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
const colorUniformLocation = gl.getUniformLocation(program, 'color');
const positionsBuffer = gl.createBuffer();
const indicesBuffer = gl.createBuffer();
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
// position & indices
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(geometry.positions), gl.STATIC_DRAW);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(geometry.indices), gl.STATIC_DRAW);
// STEP 4 (create UBO)
// bound to location 0
const perScene = new UniformBuffer(gl, [
...vec4.create(), // color 1
...vec4.create(), // color 2
], 0);
// bound to location 1 ?
const perModel = new UniformBuffer(gl, [
...vec4.create(), // color 3
], 3);
// STEP 5 (add instances)
for (let i = 0; i < 1; i++) {
renderList.push({
id: i,
vao: vao,
program: program,
color: [0, 1, 1],
});
}
// STEP 6 (draw)
gl.clearColor(0, 0, 0, 0);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, canvas.width, canvas.height);
perScene.update(gl, [
...vec4.fromValues(1, 0, 0, 1),
...vec4.fromValues(0, 1, 0, 1),
]);
for (let i = 0; i < renderList.length; i++) {
const current = renderList[i];
gl.useProgram(current.program);
gl.bindVertexArray(current.vao);
// update perObject
perModel.update(gl, [
...vec4.fromValues(0, 0, 1, 1),
]);
gl.drawElements(gl.TRIANGLES, geometry.indices.length, gl.UNSIGNED_SHORT, 0);
// unbind
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
console.log('compiled!');
canvas {
background-color: black;
}
<canvas id="canvas"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>
Shouldn't I be seeing a white square since all colours added up result in a vec4(1.0, 1.0, 1.0, 1.0)? (jsfiddle line 41)
What am I doing wrong?
Thanks
So, the first thing you're doing wrong is you're not calling gl.getUniformBlockIndex. Just like uniform you have to query the location or in this case the index of each block.
The second thing is block uniforms are indirected one level and you need to call gl.uniformBlockBinding(program, uniformBlockIndex, uniformBufferIndex);
uniformBlockIndex is the index you got from gl.getUniformBlockIndex. uniformBufferIndex similar to a texture unit. There are N uniform buffer indices. You can choose any buffer index from 0 to MAX_UNIFORM_BUFFER_BINDINGS - 1.
This indirection helps if you have one program that uses blocks A, B and another that uses A and C. In this case block A might have a different index in the 2 programs but you have it pull its values from the same uniformBufferIndex.
Note that this state is per program state so can probably set it at init time if you plan to always use the same uniform buffer index for the same uniform block.
To spell it out even more. You have a shader program. It has state
var someProgram = {
uniforms: {
projectionMatrix: [1, 0, 0, 0, 0, ... ], // etc
},
uniformBlockIndcies[ // one per uniform block
0,
0,
0,
],
...
}
Next you have uniform buffer indices which are global state
glState = {
textureUnits: [ ... ],
uniformBuffers: [ null, null, null ..., ],
};
You tell the program for each uniform buffer block, which uniform buffer index to use with gl.uniformBlockBinding. You then bind a buffer to that index with gl.bindBufferBase or gl.bindBufferRange.
It's very similar to telling a program which texture unit to use and then binding a texture to that unit. When you do this, at init time or render time is really up to you. In my mind it seems more likely I could decide at init time that my perScene stuff is always on buffer index 0 and perModel stuff at index 1 and therefore I could set them up the program parts (the calls to gl.uniformBlockBinding) at init time.
class UniformBuffer {
constructor(gl, data, boundLocation = 0) {
this.boundLocation = boundLocation;
this.data = new Float32Array(data);
this.buffer = gl.createBuffer();
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
update(gl, data, offset = 0) {
this.data.set(data, offset);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
}
};
const vertex = `#version 300 es
uniform perScene {
vec4 color1;
vec4 color2;
};
uniform perModel {
vec4 color3;
};
in vec3 a_position;
out vec3 v_color;
void main() {
gl_Position = vec4(a_position, 1.0);
v_color = color1.rgb + color2.rgb + color3.rgb;
}
`;
const fragment = `#version 300 es
precision highp float;
precision highp int;
in vec3 v_color;
out vec4 outColor;
void main() {
outColor = vec4(v_color, 1.0);
}
`;
const geometry = {
positions: [-0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0],
indices: [0, 2, 1, 1, 2, 3],
};
const renderList = [];
// STEP 1 (create canvas)
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl2");
if (!gl) {
console.log('no webgl2 buddy');
}
// STEP 2 (create program)
const v = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(v, vertex);
gl.compileShader(v);
const f = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(f, fragment);
gl.compileShader(f);
const program = gl.createProgram();
gl.attachShader(program, v);
gl.attachShader(program, f);
gl.linkProgram(program);
// STEP 3 (create VAO)
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
const colorUniformLocation = gl.getUniformLocation(program, 'color');
const positionsBuffer = gl.createBuffer();
const indicesBuffer = gl.createBuffer();
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
// position & indices
gl.enableVertexAttribArray(positionAttributeLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(geometry.positions), gl.STATIC_DRAW);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(geometry.indices), gl.STATIC_DRAW);
// STEP 4 (create UBO)
// bound to location 0
const perScene = new UniformBuffer(gl, [
...vec4.create(), // color 1
...vec4.create(), // color 2
], 0);
// bound to location 1 ?
const perModel = new UniformBuffer(gl, [
...vec4.create(), // color 3
], 1);
gl.uniformBlockBinding(program, gl.getUniformBlockIndex(program, "perScene"), perScene.boundLocation);
gl.uniformBlockBinding(program, gl.getUniformBlockIndex(program, "perModel"), perModel.boundLocation);
// STEP 5 (add instances)
for (let i = 0; i < 1; i++) {
renderList.push({
id: i,
vao: vao,
program: program,
color: [0, 1, 1],
});
}
// STEP 6 (draw)
gl.clearColor(0, 0, 0, 0);
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, canvas.width, canvas.height);
perScene.update(gl, [
...vec4.fromValues(1, 0, 0, 1),
...vec4.fromValues(0, 1, 0, 1),
]);
for (let i = 0; i < renderList.length; i++) {
const current = renderList[i];
gl.useProgram(current.program);
gl.bindVertexArray(current.vao);
// update perObject
perModel.update(gl, [
...vec4.fromValues(0, 0, 1, 1),
]);
gl.drawElements(gl.TRIANGLES, geometry.indices.length, gl.UNSIGNED_SHORT, 0);
// unbind
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
console.log('compiled!');
canvas {
background-color: black;
}
<canvas id="canvas"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>
In this example there are 5 uniform blocks.
the shared matrices like projection and view and viewProjection
the per model matrices like world and worldInverseTransform
the per light info like lightPosition and lightColor.
There are 2 lights so the 4th block is similar to the 3rd
the material data like ambient color, specularity, etc..
I'm not saying that's the perfect setup. I really have no idea. But it's pretty common to make something called a "material" and share that material among more than one model so that's like a perMaterial block which is different from a perModel block. It's also common to share lighting info. I don't know what the ideal setup is, just pointing out that perScene and perModel might not be enough for fairly common situations.
One other thing, this line
// unbind
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
makes no sense. ELEMENT_ARRAY_BUFFER is part of the VAO state.
As gman said, get the index of the uniform block and then bind it with the gl.bindBufferBase
Your updated class should look something like:
class UniformBuffer {
constructor(gl, data, program, uniformName, targetIndex = 0) {
this.data = new Float32Array(data);
const boundLocation = gl.getUniformBlockIndex(program, uniformName);
this.buffer = gl.createBuffer();
gl.bindBufferBase(gl.UNIFORM_BUFFER, boundLocation, this.buffer);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
}
update(gl, data, offset = 0) {
this.data.set(data, offset);
gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
gl.bindBuffer(gl.UNIFORM_BUFFER, null);
}
};
I have a program that was originally working completely fine that would draw a triangle using the go-gl OpenGL wrapper for Go. In the process of playing with the code things started to get weird. Sometimes the shape would be rendered, and then it just wouldn't. Sometimes saving the file then running the code again would work, sometimes that also failed. In this process there were no changes to made to the file from working to broken. The glfw window still shows up with a background color and the vertices array I use is populated. I am not sure whether this is a simple error in my code or if its something hardware related.
Not sure if this helps, but I am using the latest Atom editor with the Go-Plus plugin. Thanks in advance for the help!
package main
import (
"fmt"
"log"
"runtime"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/go-gl/glfw/v3.1/glfw"
)
const vertexSource = `
#version 330
in vec2 position;
void main() {
gl_Position = vec4(position,0.0, 1.0);
}
` + "\x00"
const fragmentSource = `
#version 330
uniform vec3 triangleColor;
out vec4 outputColor;
void main() {
outputColor = vec4(triangleColor,1.0);
}
` + "\x00"
var vao, vbo uint32
var vertices = []float32{
0.0, 0.5,
0.5, -0.5,
-0.5, -0.5,
}
func init() {
runtime.LockOSThread()
}
func main() {
if err := glfw.Init(); err != nil {
log.Fatalln("failed to initalize GL window:", err)
}
defer glfw.Terminate()
glfw.WindowHint(glfw.Resizable, glfw.True)
glfw.WindowHint(glfw.ContextVersionMajor, 4)
glfw.WindowHint(glfw.ContextVersionMinor, 1)
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
windowHeight := 800
windowWidth := 800
window, err := glfw.CreateWindow(windowWidth, windowHeight, "HelloGL2.0", nil, nil)
if err != nil {
panic(err)
}
window.MakeContextCurrent()
if err := gl.Init(); err != nil {
panic(err)
} else {
fmt.Println("OpenGL Version:", gl.GoStr(gl.GetString(gl.VERSION)))
}
gl.GenVertexArrays(1, &vao)
gl.BindVertexArray(vao)
gl.GenBuffers(1, &vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
gl.BufferData(gl.ARRAY_BUFFER, len(vertices), gl.Ptr(vertices), gl.STREAM_DRAW)
vertexShader := gl.CreateShader(gl.VERTEX_SHADER)
vcsources, free := gl.Strs(vertexSource)
gl.ShaderSource(vertexShader, 1, vcsources, nil)
free()
gl.CompileShader(vertexShader)
fragmentShader := gl.CreateShader(gl.FRAGMENT_SHADER)
fcsources, free := gl.Strs(fragmentSource)
gl.ShaderSource(fragmentShader, 1, fcsources, nil)
gl.CompileShader(fragmentShader)
shaderProgram := gl.CreateProgram()
gl.AttachShader(shaderProgram, vertexShader)
gl.AttachShader(shaderProgram, fragmentShader)
gl.BindFragDataLocation(shaderProgram, 0, gl.Str("outputColor\x00"))
gl.LinkProgram(shaderProgram)
gl.UseProgram(shaderProgram)
posAttrib := uint32(gl.GetAttribLocation(shaderProgram, gl.Str("position\x00")))
gl.EnableVertexAttribArray(posAttrib)
gl.VertexAttribPointer(posAttrib, 2.0, gl.FLOAT, false, 0.0, gl.PtrOffset(0))
gl.GetUniformLocation(shaderProgram, gl.Str("triangleColor\x00"))
//GL_STATIC_DRAW: The vertex data will be uploaded once and drawn many times (e.g. the world).
//GL_DYNAMIC_DRAW: The vertex data will be changed from time to time, but drawn many times more than that.
//GL_STREAM_DRAW: The vertex data will change almost every time it's drawn (e.g. user interface).
for !window.ShouldClose() {
//gl.Uniform3f(uniColor, 2.0, 0.0, 1.0)
gl.ClearColor(0.9, 1.0, 0.3, 1.0)
gl.Clear(gl.COLOR_BUFFER_BIT)
gl.DrawArrays(gl.TRIANGLES, 0, 3)
window.SwapBuffers()
glfw.PollEvents()
}
gl.DeleteProgram(shaderProgram)
gl.DeleteShader(fragmentShader)
gl.DeleteShader(vertexShader)
gl.DeleteBuffers(1, &vbo)
gl.DeleteVertexArrays(1, &vao)
fmt.Println("Exiting!")
window.Destroy()
}
Your code gives me:
./gl.go:76: undefined: gl.Strs
./gl.go:81: undefined: gl.Strs
Consider using my code for loading and compiling your shaders:
func NewProgram(vertexShaderSource, fragmentShaderSource string) (uint32, error) {
vertexShader, err := CompileShader(vertexShaderSource, gl.VERTEX_SHADER)
if err != nil {
return 0, err
}
fragmentShader, err := CompileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
if err != nil {
return 0, err
}
program := gl.CreateProgram()
gl.AttachShader(program, vertexShader)
gl.AttachShader(program, fragmentShader)
gl.LinkProgram(program)
var status int32
gl.GetProgramiv(program, gl.LINK_STATUS, &status)
if status == gl.FALSE {
var logLength int32
gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, &logLength)
log := strings.Repeat("\x00", int(logLength+1))
gl.GetProgramInfoLog(program, logLength, nil, gl.Str(log))
return 0, errors.New(fmt.Sprintf("failed to link program: %v", log))
}
gl.DeleteShader(vertexShader)
gl.DeleteShader(fragmentShader)
return program, nil
}
func CompileShader(source string, shaderType uint32) (uint32, error) {
shader := gl.CreateShader(shaderType)
csource := gl.Str(source)
gl.ShaderSource(shader, 1, &csource, nil)
gl.CompileShader(shader)
var status int32
gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
if status == gl.FALSE {
var logLength int32
gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
log := strings.Repeat("\x00", int(logLength+1))
gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
return 0, fmt.Errorf("failed to compile %v: %v", source, log)
}
return shader, nil
}
I am starting to learn OpenGL now, using the Go programming language (I just couldn't get working with C/C++ on my Windows machine), and so far I've managed to display some rotating cubes in the screen with textures mainly copying and pasting code from tutorials. I've learned a lot, though, but I just can't get some text on the screen with this code that I wrote on my own. I've looked up many tutorials and questions but nothing seems to work, and I suspect there is something wrong with the vertices because I'm pretty sure the textures coordinates are correct and still there's nothing showing up in the screen. Here's the code:
package game
import (
"fmt"
"io/ioutil"
"image"
"image/draw"
"github.com/go-gl/gl/v3.3-core/gl"
mgl "github.com/go-gl/mathgl/mgl32"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
type GameFont struct {
loaded bool
vao uint32
vbo VBOData
pix float32
Texture *Texture
Shader ShaderProgram
}
// Load a TrueType font from a file and generate a texture
// with all important characters.
func (f *GameFont) Load(path string, pix float32) {
contents, err := ioutil.ReadFile(path)
if err != nil {
fmt.Println("Could not read font file: " + path)
panic(err)
}
fontFace, err := truetype.Parse(contents)
if err != nil {
fmt.Println("Could not parse font file: " + path)
panic(err)
}
// Create a texture for the characters
// Find the next power of 2 for the texture size
size := nextP2(int(pix * 16))
fg, bg := image.White, image.Black
rgba := image.NewRGBA(image.Rect(0, 0, size, size))
draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src)
d := &font.Drawer{
Dst: rgba,
Src: fg,
Face: truetype.NewFace(fontFace, &truetype.Options{
Size: float64(pix),
DPI: 72,
Hinting: font.HintingNone,
}),
}
// Some GL preps
gl.GenVertexArrays(1, &f.vao)
gl.BindVertexArray(f.vao)
f.vbo.Create()
f.vbo.Bind()
f.Shader = newShaderProgram("data/shaders/font.vert", "data/shaders/font.frag")
f.Shader.Use()
f.Shader.SetUniform("tex", 0)
// Create vertex data (and coordinates in the texture) for each character
// All characters below 32 are useless
for i := 32; i < 128; i++ {
c := string(rune(i))
x, y := i % 16, i / 16
// Draw the character on the texture
d.Dot = fixed.P(x * int(pix), y * int(pix))
d.DrawString(c)
// Vertices
quads := []float32{
0, 0,
0, pix,
pix, 0,
pix, pix,
}
norm := func(n int) float32 {
return float32(n) / 16.0
}
// Texture coordinates (normalized)
texQuads := []float32{
norm(x), 1.0 - norm(y + 1),
norm(x), 1.0 - norm(y),
norm(x + 1), 1.0 - norm(y + 1),
norm(x + 1), 1.0 - norm(y),
}
for v := 0; v < 8; v += 2 {
vQuads, vTexQuads := quads[v:(v+2)], texQuads[v:(v+2)]
// Data is like (X, Y, U, V)
f.vbo.AppendData(vQuads, 2)
f.vbo.AppendData(vTexQuads, 2)
}
}
// Upload data to GPU and we're done
f.Texture = newTextureFromRGBA(rgba)
f.Texture.Bind()
f.Texture.SetGLParam(gl.TEXTURE_MIN_FILTER, gl.LINEAR)
f.Texture.SetGLParam(gl.TEXTURE_MAG_FILTER, gl.LINEAR)
f.Texture.Upload()
f.vbo.UploadData(gl.STATIC_DRAW)
gl.EnableVertexAttribArray(0)
gl.VertexAttribPointer(0, 2, gl.FLOAT, false, 4*4, gl.PtrOffset(0))
gl.EnableVertexAttribArray(1)
gl.VertexAttribPointer(1, 2, gl.FLOAT, false, 4*4, gl.PtrOffset(2*4))
f.loaded = true
}
// Render a text using the font
func (f *GameFont) Render(text string, x, y int, pix float32, color mgl.Vec4) {
if !f.loaded {
return
}
gl.Disable(gl.DEPTH_TEST)
gl.Enable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
gl.BindVertexArray(f.vao)
f.Shader.Use()
f.Shader.SetUniform("projection", mgl.Ortho2D(0, _screen.Width, 0, _screen.Height))
f.Shader.SetUniform("color", color)
f.Texture.Bind()
scale := pix / f.pix
for i := 0; i < len(text); i++ {
index := rune(text[i])
model := mgl.Ident4().Mul4(mgl.Scale3D(scale, scale, 0))
model = model.Add(mgl.Translate3D(float32(x) + float32(i) * pix, float32(y), 0))
f.Shader.SetUniform("model", model)
gl.DrawArrays(gl.TRIANGLE_STRIP, (32-index)*4, 4)
}
gl.Enable(gl.DEPTH_TEST)
gl.Disable(gl.BLEND)
}
Here's the shaders:
Vertex shader
#version 330
uniform mat4 projection;
uniform mat4 model;
layout (location = 0) in vec2 vert;
layout (location = 1) in vec2 vertTexCoord;
out vec2 fragTexCoord;
void main() {
fragTexCoord = vertTexCoord;
gl_Position = projection * model * vec4(vert, 0, 1);
}
Fragment shader
#version 330
uniform sampler2D tex;
uniform vec4 color;
in vec2 fragTexCoord;
out vec4 outputColor;
void main() {
outputColor = color * texture(tex, fragTexCoord);
}
Every "component" of the GameFont struct is working properly (I've used them with the rotating cubes), so every function calls the GL corresponding one.
Also the texture is being drawed correctly, I've saved it to the disk and it looks like this:
And still, there's no text on the 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