From 2cc809516f2ca352209d04a6e54ec119f233922d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 25 Aug 2024 15:56:44 +0900 Subject: [PATCH] ebiten: add Vertex.Custom0 to Custom3 Closes #2640 --- image.go | 11 +++++ internal/graphics/shader.go | 4 +- internal/graphics/vertex.go | 2 +- internal/shader/shader.go | 20 +++++++-- shader_test.go | 87 +++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 7 deletions(-) diff --git a/image.go b/image.go index aa2daf9bf..9fc5938c4 100644 --- a/image.go +++ b/image.go @@ -295,6 +295,13 @@ type Vertex struct { ColorG float32 ColorB float32 ColorA float32 + + // Custom0/Custom1/Custom2/Custom3 represents general-purpose values passed to the shader. + // In order to use them, Fragment must have an additional vec4 argument. + Custom0 float32 + Custom1 float32 + Custom2 float32 + Custom3 float32 } var _ [0]byte = [unsafe.Sizeof(Vertex{}) - unsafe.Sizeof(float32(0))*graphics.VertexFloatCount]byte{} @@ -668,6 +675,10 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader vs[i*graphics.VertexFloatCount+5] = v.ColorG vs[i*graphics.VertexFloatCount+6] = v.ColorB vs[i*graphics.VertexFloatCount+7] = v.ColorA + vs[i*graphics.VertexFloatCount+8] = v.Custom0 + vs[i*graphics.VertexFloatCount+9] = v.Custom1 + vs[i*graphics.VertexFloatCount+10] = v.Custom2 + vs[i*graphics.VertexFloatCount+11] = v.Custom3 } is := i.ensureTmpIndices(len(indices)) diff --git a/internal/graphics/shader.go b/internal/graphics/shader.go index 8553cb313..7a5686f92 100644 --- a/internal/graphics/shader.go +++ b/internal/graphics/shader.go @@ -154,8 +154,8 @@ func imageSrc%[1]dAt(pos vec2) vec4 { shaderSuffix += ` var __projectionMatrix mat4 -func __vertex(dstPos vec2, srcPos vec2, color vec4) (vec4, vec2, vec4) { - return __projectionMatrix * vec4(dstPos, 0, 1), srcPos, color +func __vertex(dstPos vec2, srcPos vec2, color vec4, custom vec4) (vec4, vec2, vec4, vec4) { + return __projectionMatrix * vec4(dstPos, 0, 1), srcPos, color, custom } ` return shaderSuffix, nil diff --git a/internal/graphics/vertex.go b/internal/graphics/vertex.go index 95d7768b1..6d59f31c8 100644 --- a/internal/graphics/vertex.go +++ b/internal/graphics/vertex.go @@ -39,7 +39,7 @@ const ( ) const ( - VertexFloatCount = 8 + VertexFloatCount = 12 ) var ( diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 526e16820..19c60b0d0 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -345,10 +345,10 @@ func (cs *compileState) parse(f *ast.File) { // Check varying variables. // In testings, there might not be vertex and fragment entry points. if len(vertexOutParams) > 0 && len(fragmentInParams) > 0 { - if len(vertexOutParams) != len(fragmentInParams) { - cs.addError(0, "the number of vertex entry point's returning values and the number of fragment entry point's params must be the same") - } for i, t := range vertexOutParams { + if len(fragmentInParams) <= i { + break + } if !t.Equal(&fragmentInParams[i]) { cs.addError(0, "vertex entry point's returning value types and fragment entry point's param types must match") } @@ -373,7 +373,7 @@ func (cs *compileState) parse(f *ast.File) { return } - // Set attribute varying veraibles. + // Set attribute and varying veraibles. cs.ir.Attributes = append(cs.ir.Attributes, vertexInParams...) if len(vertexOutParams) > 0 { // TODO: Check that these params are not arrays or structs @@ -820,6 +820,18 @@ func (cs *compileState) parseFunc(block *block, d *ast.FuncDecl) (function, bool } inParams, outParams, returnType := cs.parseFuncParams(block, d.Name.Name, d) + if d.Name.Name == cs.fragmentEntry { + // The 0th inParams is a special variable for position and is not included in varying variables. + if diff := len(cs.ir.Varyings) - (len(inParams) - 1); diff > 0 { + // inParams is not enough when the vertex shader has more returning values than the fragment shader's arguments. + for i := 0; i < diff; i++ { + inParams = append(inParams, variable{ + name: "_", + typ: cs.ir.Varyings[len(inParams)-1+i], + }) + } + } + } b, ok := cs.parseBlock(block, d.Name.Name, d.Body.List, inParams, outParams, returnType, true) if !ok { return function{}, false diff --git a/shader_test.go b/shader_test.go index e04e7749e..70c292524 100644 --- a/shader_test.go +++ b/shader_test.go @@ -2595,3 +2595,90 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { } } } + +func TestShaderCustomValues(t *testing.T) { + const w, h = 16, 16 + + dst := ebiten.NewImage(w, h) + s, err := ebiten.NewShader([]byte(`//kage:unit pixels + +package main + +func Fragment(dstPos vec4, srcPos vec2, color vec4, custom vec4) vec4 { + return custom +} +`)) + if err != nil { + t.Fatal(err) + } + + clr := color.RGBA{R: 0x10, G: 0x20, B: 0x30, A: 0x40} + dst.DrawTrianglesShader([]ebiten.Vertex{ + { + DstX: 0, + DstY: 0, + SrcX: 0, + SrcY: 0, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + Custom0: float32(clr.R) / 0xff, + Custom1: float32(clr.G) / 0xff, + Custom2: float32(clr.B) / 0xff, + Custom3: float32(clr.A) / 0xff, + }, + { + DstX: w, + DstY: 0, + SrcX: w, + SrcY: 0, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + Custom0: float32(clr.R) / 0xff, + Custom1: float32(clr.G) / 0xff, + Custom2: float32(clr.B) / 0xff, + Custom3: float32(clr.A) / 0xff, + }, + { + DstX: 0, + DstY: h, + SrcX: 0, + SrcY: h, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + Custom0: float32(clr.R) / 0xff, + Custom1: float32(clr.G) / 0xff, + Custom2: float32(clr.B) / 0xff, + Custom3: float32(clr.A) / 0xff, + }, + { + DstX: w, + DstY: h, + SrcX: w, + SrcY: h, + ColorR: 1, + ColorG: 1, + ColorB: 1, + ColorA: 1, + Custom0: float32(clr.R) / 0xff, + Custom1: float32(clr.G) / 0xff, + Custom2: float32(clr.B) / 0xff, + Custom3: float32(clr.A) / 0xff, + }, + }, []uint16{0, 1, 2, 1, 2, 3}, s, nil) + + for j := 0; j < h; j++ { + for i := 0; i < w; i++ { + got := dst.At(i, j).(color.RGBA) + want := clr + if !sameColors(got, want, 2) { + t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want) + } + } + } +}