// vim: shiftwidth=4 tabstop=4 noexpandtab package main import ( "fmt" "log" "strings" "github.com/go-gl/gl/v4.6-core/gl" "github.com/go-gl/mathgl/mgl32" ) var ( vertices []float32 vertex_normals []float32 ) const ( // vertex shader to draw points VERTEX_SHADER = ` #version 410 in vec3 vp_model; // vertex position in model coordinate system in vec3 vn_model; // vertex normal in model coordinate system uniform mat4 trafo; // one single transformation matrix out vec3 vn_eye; // vertex normal in eye coordinate system void main() { // 0.0 because translation is ignored vn_eye = vec3(trafo * vec4(vn_model, 0.0)); gl_Position = trafo * vec4(vp_model, 1.0); } ` + "\x00" // fragment shader to draw surfaces FRAGMENT_SHADER = ` #version 410 in vec3 vn_eye; // vertex normal in eye coordinate system out vec4 frag_colour; void main() { vec3 ambient = vec3(0.3, 0.3, 0.3); // ambient colour is static vec3 light_vector_eye = vec3(1.0, 0.1, -1.0); vec3 diffuse = (vec3(1, 1, 1) - ambient) * max(dot(normalize(vn_eye), normalize(light_vector_eye)), 0.0); // hard-coded vector is color in RGB format: vec3 color = (ambient + diffuse) * vec3(0, 0, 1); frag_colour = vec4(color, 1.0); // RGBA color format } ` + "\x00" ) // Graphics is a struct to save OpenGL-related data like shaders. type Graphics struct { version string vao uint32 vertexShader uint32 fragmentShader uint32 program uint32 trafo mgl32.Mat4 trafoUniform int32 } // newGraphics initializes a new Graphics struct and returns it. func newGraphics() Graphics { var graphics Graphics = Graphics{} graphics.trafo = mgl32.Ident4() // init OpenGL and save/log version log.Println("OpenGL init") err := gl.Init() if err != nil { log.Fatal(err) } graphics.version = gl.GoStr(gl.GetString(gl.VERSION)) log.Println("OpenGL version", graphics.version) // compile shaders graphics.vertexShader, err = compileShader(VERTEX_SHADER, gl.VERTEX_SHADER) if err != nil { log.Fatal(err) } graphics.fragmentShader, err = compileShader(FRAGMENT_SHADER, gl.FRAGMENT_SHADER) if err != nil { log.Fatal(err) } // create GL program graphics.program = gl.CreateProgram() gl.AttachShader(graphics.program, graphics.vertexShader) gl.AttachShader(graphics.program, graphics.fragmentShader) gl.Enable(gl.DEPTH_TEST) gl.LinkProgram(graphics.program) // create VAO graphics.vao = makeVao(vertices, vertex_normals) // create transformation matrix gl.UniformMatrix4fv(graphics.trafoUniform, 1, false, &graphics.trafo[0]) return graphics } // draw executes the rendering process for a Graphics struct one time. func (graphics Graphics) draw() { gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) gl.UseProgram(graphics.program) gl.UniformMatrix4fv(graphics.trafoUniform, 1, false, &graphics.trafo[0]) gl.BindVertexArray(graphics.vao) // or alternatively POINTS, LINES, LINE_STRIP, LINE_LOOP, TRIANGLES, // TRIANGLE_STRIP, TRIANGLE_FAN gl.DrawArrays(gl.TRIANGLES, 0, int32(len(vertices)/3)) } // makeVao initializes and returns a vertex array from the points provided. func makeVao(points []float32, normals []float32) uint32 { log.Println("Creating VAO") // vertex buffer object (VBO) for positions: var position_vbo uint32 // VBO ID gl.GenBuffers(1, &position_vbo) gl.BindBuffer(gl.ARRAY_BUFFER, position_vbo) gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW) // vertex buffer object (VBO) for normals: var normal_vbo uint32 // VBO ID gl.GenBuffers(1, &normal_vbo) gl.BindBuffer(gl.ARRAY_BUFFER, normal_vbo) gl.BufferData(gl.ARRAY_BUFFER, 4*len(normals), gl.Ptr(normals), gl.STATIC_DRAW) // vertex array objects (VAO) to combine VBOs: var vao uint32 // VAO ID gl.GenVertexArrays(1, &vao) gl.BindVertexArray(vao) // connect position_vbo to vao gl.BindBuffer(gl.ARRAY_BUFFER, position_vbo) // tell GL to use 3D float vectors: gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil) gl.EnableVertexAttribArray(0) // connect normal_vbo to vao gl.BindBuffer(gl.ARRAY_BUFFER, normal_vbo) // tell GL to use 3D float vectors: gl.VertexAttribPointer(1, 3, gl.FLOAT, false, 0, nil) gl.EnableVertexAttribArray(1) return vao } // compileShaders compiles the shader from source code. func compileShader(source string, shaderType uint32) (uint32, error) { log.Println("Compiling shader") shader := gl.CreateShader(shaderType) csources, free := gl.Strs(source) 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", source, log) } return shader, nil } // setTrafo sets the current transformation matrix of the given Graphics struct. func (graphics *Graphics) setTrafo(trafo mgl32.Mat4) { graphics.trafo = trafo }