From c53d6d94e4ee8ca01d857ed5f86899be048ef7f4 Mon Sep 17 00:00:00 2001 From: xengineering Date: Mon, 31 May 2021 10:41:31 +0200 Subject: Implement ambient and diffuse Lighting --- Makefile | 2 +- README.md | 6 +++++- app.go | 2 +- data/example.png | Bin 10424 -> 3180 bytes graphics.go | 55 +++++++++++++++++++++++++++++++++++++++++-------------- main.go | 2 +- stl.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++---- 7 files changed, 98 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 447434f..75f31c8 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ clean: rm -rf build debug: - go run ./... data/L.stl + go run ./... -debug data/L.stl install: all mkdir -p $(DESTDIR)$(PREFIX)/bin diff --git a/README.md b/README.md index 503834f..d64b4c3 100644 --- a/README.md +++ b/README.md @@ -18,15 +18,19 @@ Have a look at `stlscope -h` to learn about further options. ## Development Milestones Done (most recent first): +- [x] improve graphics to better display the model (ambient and diffuse lighting) - [x] package for Arch Linux (get a PKGBUILD file from [here](https://src.xengineering.eu/xengineering/pkgbuilds/src/branch/master/stlscope)) - [x] implement Makefile to support building - [x] implement minimal viable product To do (most important first): +- [ ] allow window resizing - [ ] implement controls to modify view -- [ ] improve graphics to better display the model - [ ] add support for ASCII STL format - [ ] improve performance with GPU-based matrix-matrix-multiplication +- [ ] create icon for stlscope +- [ ] release version 1.0.0 +- [ ] add desktop support ## Further Ressources diff --git a/app.go b/app.go index 22548a3..30dfd44 100644 --- a/app.go +++ b/app.go @@ -58,7 +58,7 @@ func (application *App) handle() { // generate and set transformation trafo := application.homeTrafo - trafo = mgl32.HomogRotate3D(float32(glfw.GetTime()) * 3, mgl32.Vec3{0, 1, 0}).Mul4(trafo) // apply time-based rotation + trafo = mgl32.HomogRotate3D(float32(glfw.GetTime()) * 0.4 * 6.282, mgl32.Vec3{0.1, 1, 0}).Mul4(trafo) // apply time-based rotation application.graphics.setTrafo(trafo) } diff --git a/data/example.png b/data/example.png index 2d8a256..26935de 100644 Binary files a/data/example.png and b/data/example.png differ diff --git a/graphics.go b/graphics.go index 7107926..fc195eb 100644 --- a/graphics.go +++ b/graphics.go @@ -13,6 +13,7 @@ import ( var ( vertices []float32 + vertex_normals []float32 ) const ( @@ -21,21 +22,33 @@ const ( VERTEX_SHADER = ` #version 410 - uniform mat4 trafo; + in vec3 vp_model; // vertex position in model coordinate system + in vec3 vn_model; // vertex normal in model coordinate system - in vec3 vp; + uniform mat4 trafo; // one single transformation matrix + + out vec3 vn_eye; // vertex normal in eye coordinate system void main() { - gl_Position = trafo * vec4(vp, 1.0); + vn_eye = vec3(trafo * vec4(vn_model, 0.0)); // 0.0 because translation is ignored + 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() { - frag_colour = vec4(0, 0, 1, 1); // RGBA color format + 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); + vec3 color = (ambient + diffuse) * vec3(0, 0, 1); // hard-coded vector is color in RGB format + frag_colour = vec4(color, 1.0); // RGBA color format } ` + "\x00" ) @@ -79,10 +92,11 @@ func newGraphics() Graphics { 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) + graphics.vao = makeVao(vertices, vertex_normals) // create transformation matrix //graphics.trafo = mgl32.HomogRotate3D(float32(glfw.GetTime()) * OMEGA, mgl32.Vec3{ROT_X, ROT_Y, ROT_Z}) @@ -100,26 +114,39 @@ func (graphics Graphics) draw() { gl.UniformMatrix4fv(graphics.trafoUniform, 1, false, &graphics.trafo[0]) gl.BindVertexArray(graphics.vao) - gl.DrawArrays(gl.LINE_STRIP, 0, int32(len(vertices)/3)) // POINTS, LINES, LINE_STRIP, LINE_LOOP, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN + gl.DrawArrays(gl.TRIANGLES, 0, int32(len(vertices)/3)) // POINTS, LINES, LINE_STRIP, LINE_LOOP, TRIANGLES, TRIANGLE_STRIP, TRIANGLE_FAN } // makeVao initializes and returns a vertex array from the points provided. -func makeVao(points []float32) uint32 { +func makeVao(points []float32, normals []float32) uint32 { log.Println("Creating VAO") - // start with vertex buffer object (VBO): - var vbo uint32 // VBO ID - gl.GenBuffers(1, &vbo) - gl.BindBuffer(gl.ARRAY_BUFFER, vbo) + // 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 array objects (VAO) are a feature of newer GL implementations: + // 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) - gl.EnableVertexAttribArray(0) - gl.BindBuffer(gl.ARRAY_BUFFER, vbo) + + // connect position_vbo to vao + gl.BindBuffer(gl.ARRAY_BUFFER, position_vbo) gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil) // tell GL to use 3D float vectors + gl.EnableVertexAttribArray(0) + + // connect normal_vbo to vao + gl.BindBuffer(gl.ARRAY_BUFFER, normal_vbo) + gl.VertexAttribPointer(1, 3, gl.FLOAT, false, 0, nil) // tell GL to use 3D float vectors + gl.EnableVertexAttribArray(1) return vao } diff --git a/main.go b/main.go index 004fbf3..084b8ec 100644 --- a/main.go +++ b/main.go @@ -28,7 +28,7 @@ func main() { if err != nil { log.Fatal(err) } - vertices = stl.toVertices() + vertices,vertex_normals = stl.toVertices() // lock this program to one OS thread (details: https://golang.org/pkg/runtime/#LockOSThread) log.Println("Locking OS thread") diff --git a/stl.go b/stl.go index 35e69eb..60ada3e 100644 --- a/stl.go +++ b/stl.go @@ -20,6 +20,8 @@ type StlModel struct { surface Surface } +type Vector3 [3]float32 + // read and parse a given binary STL file func ReadBinaryStlFile(filePath string) (StlModel, error) { @@ -75,9 +77,12 @@ func ParseBinaryStlTriangle(data []byte) *Triangle { // FIXME: This function sh return triangle } -func (stl StlModel) toVertices() []float32 { +func (stl StlModel) toVertices() (vertex_position []float32, vertex_normal []float32) { + + vertex_position = make([]float32, stl.numberOfTriangles * 9) + vertex_normal = make([]float32, stl.numberOfTriangles * 9) - retval := make([]float32, stl.numberOfTriangles * 9) + var point0,point1,point2,edge0,edge1,normal Vector3; for triangleIndex,triangle := range(stl.surface.triangles) { @@ -85,13 +90,53 @@ func (stl StlModel) toVertices() []float32 { for scalarIndex,scalar := range(point.scalars) { - retval[triangleIndex*9+pointIndex*3+scalarIndex] = scalar + vertex_position[triangleIndex*9+pointIndex*3+scalarIndex] = scalar } } + // calculate normal + point0 = Vector3{triangle.points[0].scalars[0], triangle.points[0].scalars[1], triangle.points[0].scalars[2]} + point1 = Vector3{triangle.points[1].scalars[0], triangle.points[1].scalars[1], triangle.points[1].scalars[2]} + point2 = Vector3{triangle.points[2].scalars[0], triangle.points[2].scalars[1], triangle.points[2].scalars[2]} + edge0 = point1.subtract(point0) + edge1 = point2.subtract(point1) + normal = edge0.crossProduct(edge1) + normal.divideScalar(2.0) // length of normal vector corresponds to triangle area + + // save normal to each vertex of the triangle + for i := 0; i<3; i++ { + vertex_normal[triangleIndex*9+i*3+0] = normal[0] + vertex_normal[triangleIndex*9+i*3+1] = normal[1] + vertex_normal[triangleIndex*9+i*3+2] = normal[2] + } + } - return retval + return vertex_position, vertex_normal +} + +func (vector *Vector3) divideScalar(scalar float32) { + vector[0] = vector[0] / scalar + vector[1] = vector[1] / scalar + vector[2] = vector[2] / scalar +} + +func (vectorA Vector3) subtract(vectorB Vector3) (vectorC Vector3) { + + vectorC[0] = vectorA[0] - vectorB[0] + vectorC[1] = vectorA[1] - vectorB[1] + vectorC[2] = vectorA[2] - vectorB[2] + + return vectorC +} + +func (vectorA Vector3) crossProduct(vectorB Vector3) (vectorC Vector3) { + + vectorC[0] = vectorA[1] * vectorB[2] - vectorA[2] * vectorB[1] + vectorC[1] = vectorA[2] * vectorB[0] - vectorA[0] * vectorB[2] + vectorC[2] = vectorA[0] * vectorB[1] - vectorA[1] * vectorB[0] + + return vectorC } -- cgit v1.2.3-70-g09d2