ebiten: add Vertex.Custom0 to Custom3

Closes #2640
This commit is contained in:
Hajime Hoshi 2024-08-25 15:56:44 +09:00
parent bff760af01
commit 2cc809516f
5 changed files with 117 additions and 7 deletions

View File

@ -295,6 +295,13 @@ type Vertex struct {
ColorG float32 ColorG float32
ColorB float32 ColorB float32
ColorA 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{} 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+5] = v.ColorG
vs[i*graphics.VertexFloatCount+6] = v.ColorB vs[i*graphics.VertexFloatCount+6] = v.ColorB
vs[i*graphics.VertexFloatCount+7] = v.ColorA 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)) is := i.ensureTmpIndices(len(indices))

View File

@ -154,8 +154,8 @@ func imageSrc%[1]dAt(pos vec2) vec4 {
shaderSuffix += ` shaderSuffix += `
var __projectionMatrix mat4 var __projectionMatrix mat4
func __vertex(dstPos vec2, srcPos vec2, color vec4) (vec4, vec2, vec4) { func __vertex(dstPos vec2, srcPos vec2, color vec4, custom vec4) (vec4, vec2, vec4, vec4) {
return __projectionMatrix * vec4(dstPos, 0, 1), srcPos, color return __projectionMatrix * vec4(dstPos, 0, 1), srcPos, color, custom
} }
` `
return shaderSuffix, nil return shaderSuffix, nil

View File

@ -39,7 +39,7 @@ const (
) )
const ( const (
VertexFloatCount = 8 VertexFloatCount = 12
) )
var ( var (

View File

@ -345,10 +345,10 @@ func (cs *compileState) parse(f *ast.File) {
// Check varying variables. // Check varying variables.
// In testings, there might not be vertex and fragment entry points. // In testings, there might not be vertex and fragment entry points.
if len(vertexOutParams) > 0 && len(fragmentInParams) > 0 { 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 { for i, t := range vertexOutParams {
if len(fragmentInParams) <= i {
break
}
if !t.Equal(&fragmentInParams[i]) { if !t.Equal(&fragmentInParams[i]) {
cs.addError(0, "vertex entry point's returning value types and fragment entry point's param types must match") 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 return
} }
// Set attribute varying veraibles. // Set attribute and varying veraibles.
cs.ir.Attributes = append(cs.ir.Attributes, vertexInParams...) cs.ir.Attributes = append(cs.ir.Attributes, vertexInParams...)
if len(vertexOutParams) > 0 { if len(vertexOutParams) > 0 {
// TODO: Check that these params are not arrays or structs // 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) 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) b, ok := cs.parseBlock(block, d.Name.Name, d.Body.List, inParams, outParams, returnType, true)
if !ok { if !ok {
return function{}, false return function{}, false

View File

@ -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)
}
}
}
}