diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..50e6776 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,18 @@ +GOROOT := $(shell go env GOROOT) + +.PHONY: serve +serve: + @echo "demo available at http://localhost:8080" + @go run server.go + +.PHONY: triangle +triangle: + @cp "$(GOROOT)/misc/wasm/wasm_exec.js" . + @GOARCH=wasm GOOS=js go build -o demo.wasm triangle/*.go + @make serve + +.PHONY: generate-texture +generate-texture: + @cp "$(GOROOT)/misc/wasm/wasm_exec.js" . + @GOARCH=wasm GOOS=js go build -o demo.wasm generate_texture/*.go + @make serve diff --git a/examples/README.md b/examples/README.md index b84796a..6bf41c3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,23 +1,17 @@ # Simple examples using `webgl-go` -## A colored triangle - -- Copy `wasm_exec.js` from the standard `Go` installation in this folder (only once). - - ``` - cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" . - ``` - - Compile the code + - A colored triangle + ``` - GOARCH=wasm GOOS=js go build -o demo.wasm demos/triangle.go demos/shader_helpers.go + make triangle ``` -- Serve the web page + - A colored triangle using a texture generated from data ``` - go run server.go + make generate-texture ``` - Open the following URL in a browser: diff --git a/examples/generate_texture/generate_texture.go b/examples/generate_texture/generate_texture.go new file mode 100644 index 0000000..d4d64eb --- /dev/null +++ b/examples/generate_texture/generate_texture.go @@ -0,0 +1,122 @@ +package main + +import ( + "math" + "syscall/js" + + webgl "github.com/seqsense/webgl-go" +) + +const vsSource = ` +attribute vec3 position; +attribute vec2 coord; + +varying vec2 texCoord; + +void main(void) { + gl_Position = vec4(position, 1.0); + texCoord = coord; +} +` +const fsSource = ` +precision mediump float; +uniform sampler2D texture0; +varying vec2 texCoord; + +void main(void) { + gl_FragColor = texture2D(texture0, texCoord); +} +` + +var vertices = []float32{ + -0.5, -0.5, 0, + 0.5, -0.5, 0, + 0, 0.5, 0, +} + +var texCoords = []float32{ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, +} + +func run() { + canvas := js.Global().Get("document").Call("getElementById", "glcanvas") + + gl, err := webgl.New(canvas) + if err != nil { + panic(err) + } + + width := gl.Canvas.ClientWidth() + height := gl.Canvas.ClientHeight() + + var vs, fs webgl.Shader + if vs, err = initShader(gl, vsSource, gl.VERTEX_SHADER); err != nil { + panic(err) + } + + if fs, err = initShader(gl, fsSource, gl.FRAGMENT_SHADER); err != nil { + panic(err) + } + + program, err := linkShaders(gl, nil, vs, fs) + if err != nil { + panic(err) + } + + gl.UseProgram(program) + + vertexBuffer := gl.CreateBuffer() + gl.BindBuffer(gl.ARRAY_BUFFER, vertexBuffer) + gl.BufferData(gl.ARRAY_BUFFER, webgl.Float32ArrayBuffer(vertices), gl.STATIC_DRAW) + + positionLoc := gl.GetAttribLocation(program, "position") + gl.VertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0) + + texCoordsBuffer := gl.CreateBuffer() + gl.BindBuffer(gl.ARRAY_BUFFER, texCoordsBuffer) + gl.BufferData(gl.ARRAY_BUFFER, webgl.Float32ArrayBuffer(texCoords), gl.STATIC_DRAW) + texCoordsLoc := gl.GetAttribLocation(program, "coord") + gl.VertexAttribPointer(texCoordsLoc, 2, gl.FLOAT, false, 0, 0) + + texture := gl.CreateTexture() + gl.BindTexture(gl.TEXTURE_2D, texture) + textureULoc := gl.GetUniformLocation(program, "texture0") + + texWidth := 256 + texHeight := 256 + texData := make([]uint8, 4*texWidth*texHeight) + // fill the texture with some colors + firstColor := [3]float64{255, 0, 0} + secondColor := [3]float64{0, 0, 255} + for i := 0; i < texWidth; i += 1 { + for j := 0; j < texHeight; j += 1 { + index := (j*texWidth + i) * 4 + d := float64(i) / math.Max(float64(texWidth), float64(texHeight)) + for k := 0; k < 3; k++ { + texData[index+k] = uint8(firstColor[k] + d*(secondColor[k]-firstColor[k])) + } + texData[index+3] = 255 + } + } + gl.TexImage2D2(gl.TEXTURE_2D, 0, gl.RGBA, texWidth, texHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, texData) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + 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.ClearColor(0.5, 0.5, 0.5, 0.9) + gl.Clear(gl.COLOR_BUFFER_BIT) + gl.Viewport(0, 0, width, height) + + gl.EnableVertexAttribArray(positionLoc) + gl.EnableVertexAttribArray(texCoordsLoc) + gl.Uniform1i(textureULoc, 0) + gl.DrawArrays(gl.TRIANGLES, 0, len(vertices)/3) +} + +func main() { + go run() + select {} +} diff --git a/examples/demos/shader_helpers.go b/examples/generate_texture/shader_helpers.go similarity index 59% rename from examples/demos/shader_helpers.go rename to examples/generate_texture/shader_helpers.go index 4ef3b95..993552e 100644 --- a/examples/demos/shader_helpers.go +++ b/examples/generate_texture/shader_helpers.go @@ -10,24 +10,19 @@ import ( webgl "github.com/seqsense/webgl-go" ) -func initVertexShader(gl *webgl.WebGL, src string) (webgl.Shader, error) { - s := gl.CreateShader(gl.VERTEX_SHADER) +func initShader(gl *webgl.WebGL, src string, sType webgl.ShaderType) (webgl.Shader, error) { + s := gl.CreateShader(sType) gl.ShaderSource(s, src) gl.CompileShader(s) if !gl.GetShaderParameter(s, gl.COMPILE_STATUS).(bool) { compilationLog := gl.GetShaderInfoLog(s) - return webgl.Shader(js.Null()), fmt.Errorf("compile failed (VERTEX_SHADER) %v", compilationLog) - } - return s, nil -} - -func initFragmentShader(gl *webgl.WebGL, src string) (webgl.Shader, error) { - s := gl.CreateShader(gl.FRAGMENT_SHADER) - gl.ShaderSource(s, src) - gl.CompileShader(s) - if !gl.GetShaderParameter(s, gl.COMPILE_STATUS).(bool) { - compilationLog := gl.GetShaderInfoLog(s) - return webgl.Shader(js.Null()), fmt.Errorf("compile failed (FRAGMENT_SHADER) %v", compilationLog) + var sTypeStr string + if sType == gl.VERTEX_SHADER { + sTypeStr = "VERTEX_SHADER" + } else { + sTypeStr = "FRAGMENT_SHADER" + } + return webgl.Shader(js.Null()), fmt.Errorf("compile failed (%s) %v", sTypeStr, compilationLog) } return s, nil } diff --git a/examples/triangle/shader_helpers.go b/examples/triangle/shader_helpers.go new file mode 100644 index 0000000..993552e --- /dev/null +++ b/examples/triangle/shader_helpers.go @@ -0,0 +1,43 @@ +package main + +// Taken with minor modifications from https://github.com/seqsense/pcdeditor/blob/master/shader_js.go + +import ( + "errors" + "fmt" + "syscall/js" + + webgl "github.com/seqsense/webgl-go" +) + +func initShader(gl *webgl.WebGL, src string, sType webgl.ShaderType) (webgl.Shader, error) { + s := gl.CreateShader(sType) + gl.ShaderSource(s, src) + gl.CompileShader(s) + if !gl.GetShaderParameter(s, gl.COMPILE_STATUS).(bool) { + compilationLog := gl.GetShaderInfoLog(s) + var sTypeStr string + if sType == gl.VERTEX_SHADER { + sTypeStr = "VERTEX_SHADER" + } else { + sTypeStr = "FRAGMENT_SHADER" + } + return webgl.Shader(js.Null()), fmt.Errorf("compile failed (%s) %v", sTypeStr, compilationLog) + } + return s, nil +} + +func linkShaders(gl *webgl.WebGL, fbVarings []string, shaders ...webgl.Shader) (webgl.Program, error) { + program := gl.CreateProgram() + for _, s := range shaders { + gl.AttachShader(program, s) + } + if len(fbVarings) > 0 { + gl.TransformFeedbackVaryings(program, fbVarings, gl.SEPARATE_ATTRIBS) + } + gl.LinkProgram(program) + if !gl.GetProgramParameter(program, gl.LINK_STATUS).(bool) { + return webgl.Program(js.Null()), errors.New("link failed: " + gl.GetProgramInfoLog(program)) + } + return program, nil +} diff --git a/examples/demos/triangle.go b/examples/triangle/triangle.go similarity index 92% rename from examples/demos/triangle.go rename to examples/triangle/triangle.go index 6ba1089..1ab9118 100644 --- a/examples/demos/triangle.go +++ b/examples/triangle/triangle.go @@ -57,11 +57,11 @@ func run() { gl.BufferData(gl.ARRAY_BUFFER, webgl.Float32ArrayBuffer(colors), gl.STATIC_DRAW) var vs, fs webgl.Shader - if vs, err = initVertexShader(gl, vsSource); err != nil { + if vs, err = initShader(gl, vsSource, gl.VERTEX_SHADER); err != nil { panic(err) } - if fs, err = initFragmentShader(gl, fsSource); err != nil { + if fs, err = initShader(gl, fsSource, gl.FRAGMENT_SHADER); err != nil { panic(err) } @@ -84,7 +84,6 @@ func run() { gl.ClearColor(0.5, 0.5, 0.5, 0.9) gl.Clear(gl.COLOR_BUFFER_BIT) - gl.Enable(gl.DEPTH_TEST) gl.Viewport(0, 0, width, height) gl.DrawArrays(gl.TRIANGLES, 0, len(vertices)/3) } diff --git a/gl_js.go b/gl_js.go index c7cb0f1..601f1b9 100644 --- a/gl_js.go +++ b/gl_js.go @@ -376,6 +376,12 @@ func (gl *WebGL) TexImage2D(texType TextureType, level int, internalFmt, fmt Pix gl.gl.Call("texImage2D", int(texType), level, int(internalFmt), int(fmt), int(typ), img) } +func (gl *WebGL) TexImage2D2(texType TextureType, level int, internalFmt PixelFormat, width, height, border int, fmt PixelFormat, typ Type, data []uint8) { + dataJS := uint8Array.New(len(data)) + js.CopyBytesToJS(dataJS, data) + gl.gl.Call("texImage2D", int(texType), level, int(internalFmt), width, height, border, int(fmt), int(typ), dataJS) +} + func (gl *WebGL) TexParameteri(texType TextureType, param TextureParameter, val interface{}) { gl.gl.Call("texParameteri", int(texType), int(param), val) }