Skip to content

Commit

Permalink
Improvements to lighting (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
mokiat authored Oct 6, 2024
1 parent b080ced commit 514aab3
Show file tree
Hide file tree
Showing 24 changed files with 417 additions and 166 deletions.
58 changes: 57 additions & 1 deletion game/asset/dsl/provider_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dsl

import (
"fmt"
"math"
"os"

"github.com/mokiat/gog/opt"
Expand Down Expand Up @@ -146,7 +147,7 @@ func IrradianceCubeImage(imageProvider Provider[*mdl.CubeImage], opts ...Operati
}

sampleCount := cfg.sampleCount.ValueOrDefault(20)
return mdl.BuildIrradianceCubeImage(image, sampleCount), nil
return mdl.BuildIrradianceCubeImage(image, sampleCount, 0.0), nil
},

// digest function
Expand All @@ -156,6 +157,61 @@ func IrradianceCubeImage(imageProvider Provider[*mdl.CubeImage], opts ...Operati
))
}

// ReflectionCubeImages creates a reflection cube image mipmap set from the
// provided HDR skybox cube image.
func ReflectionCubeImages(imageProvider Provider[*mdl.CubeImage], opts ...Operation) Provider[[]*mdl.CubeImage] {
return OnceProvider(FuncProvider(
// get function
func() ([]*mdl.CubeImage, error) {
var cfg irradianceConfig
for _, opt := range opts {
if err := opt.Apply(&cfg); err != nil {
return nil, fmt.Errorf("failed to configure irradiance cube image: %w", err)
}
}

image, err := imageProvider.Get()
if err != nil {
return nil, fmt.Errorf("error getting image: %w", err)
}

sampleCount := cfg.sampleCount.ValueOrDefault(20)

var dimensions []int
dimension := image.Side(mdl.CubeSideFront).Width()
for dimension > 0 {
dimensions = append(dimensions, dimension)
dimension /= 2
}

mipmapCount := len(dimensions)
result := make([]*mdl.CubeImage, mipmapCount)
for i := range mipmapCount {
if i == 0 {
result[i] = image.MapTexels(func(texel mdl.Color) mdl.Color {
return mdl.Color{
R: texel.R * math.Pi * 2.0,
G: texel.G * math.Pi * 2.0,
B: texel.B * math.Pi * 2.0,
A: 1.0,
}
})
} else {
minDot := 1.0 - (float64(i) / float64(mipmapCount-1))
dimension := dimensions[i]
result[i] = mdl.BuildIrradianceCubeImage(image.Scale(dimension), sampleCount, minDot)
}
}
return result, nil
},

// digest function
func() ([]byte, error) {
return CreateDigest("reflection-cube-images", imageProvider, opts)
},
))
}

type irradianceConfig struct {
sampleCount opt.T[int]
}
Expand Down
20 changes: 13 additions & 7 deletions game/asset/dsl/provider_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,10 @@ func BuildModelResource(gltfDoc *gltf.Document, forceCollision bool) (*mdl.Model
// build textures
texturesFromIndex := make(map[int]*mdl.Texture)
for i, img := range imagesFromIndex {
texture := &mdl.Texture{}
texture := mdl.Create2DTexture(img.Width(), img.Height(), 1, mdl.TextureFormatRGBA8)
texture.SetName(img.Name())
texture.SetKind(mdl.TextureKind2D)
texture.SetFormat(mdl.TextureFormatRGBA8)
texture.SetGenerateMipmaps(true)
texture.Resize(img.Width(), img.Height())
texture.SetLayerImage(0, img)
texture.SetLayerImage(0, 0, img)
texturesFromIndex[i] = texture
}

Expand Down Expand Up @@ -227,6 +224,8 @@ func BuildModelResource(gltfDoc *gltf.Document, forceCollision bool) (*mdl.Model
if texIndex, texScale := gltfutil.NormalTextureIndexScale(gltfDoc, gltfMaterial); texIndex != nil {
normalTextureIndex = texIndex
normalScale = texScale
sampler := samplersFromIndex[*texIndex]
sampler.Texture().SetLinear(true)
} else {
normalScale = 1.0
}
Expand Down Expand Up @@ -782,8 +781,8 @@ func createPBRShader(cfg pbrShaderConfig) string {
if cfg.hasMetallicRoughnessTexture {
sourceCode += `
var metallicRoughness vec4 = sample(metallicRoughnessSampler, #vertexUV)
#metallic = metallicRoughness.b
#roughness = metallicRoughness.g
#metallic = metallicRoughness.b * metallic
#roughness = metallicRoughness.g * roughness
`
} else {
sourceCode += `
Expand All @@ -792,6 +791,13 @@ func createPBRShader(cfg pbrShaderConfig) string {
`
}

if cfg.hasNormalTexture {
sourceCode += `
var lsNormal vec4 = sample(normalSampler, #vertexUV)
#normal = mapNormal(lsNormal.xyz, normalScale)
`
}

sourceCode += `}`

return sourceCode
Expand Down
66 changes: 49 additions & 17 deletions game/asset/dsl/provider_texture.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@ func Create2DTexture(imageProvider Provider[*mdl.Image], opts ...Operation) Prov
return nil, fmt.Errorf("failed to get image: %w", err)
}

var texture mdl.Texture
texture := mdl.Create2DTexture(image.Width(), image.Height(), 1, cfg.format.ValueOrDefault(mdl.TextureFormatRGBA8))
texture.SetName(image.Name())
texture.SetKind(mdl.TextureKind2D)
texture.SetFormat(cfg.format.ValueOrDefault(mdl.TextureFormatRGBA8))
texture.SetGenerateMipmaps(cfg.mipmapping)
texture.Resize(image.Width(), image.Height())
texture.SetLayerImage(0, image)
return &texture, nil
texture.SetLayerImage(0, 0, image)
return texture, nil
},

// digest function
Expand Down Expand Up @@ -67,18 +64,15 @@ func CreateCubeTexture(cubeImageProvider Provider[*mdl.CubeImage], opts ...Opera
topImage := cubeImage.Side(mdl.CubeSideTop)
bottomImage := cubeImage.Side(mdl.CubeSideBottom)

var texture mdl.Texture
texture.SetKind(mdl.TextureKindCube)
texture.SetFormat(cfg.format.ValueOrDefault(mdl.TextureFormatRGBA16F))
texture := mdl.CreateCubeTexture(frontImage.Width(), 1, cfg.format.ValueOrDefault(mdl.TextureFormatRGBA16F))
texture.SetGenerateMipmaps(cfg.mipmapping)
texture.Resize(frontImage.Width(), frontImage.Height())
texture.SetLayerImage(0, frontImage)
texture.SetLayerImage(1, rearImage)
texture.SetLayerImage(2, leftImage)
texture.SetLayerImage(3, rightImage)
texture.SetLayerImage(4, topImage)
texture.SetLayerImage(5, bottomImage)
return &texture, nil
texture.SetLayerImage(0, 0, frontImage)
texture.SetLayerImage(0, 1, rearImage)
texture.SetLayerImage(0, 2, leftImage)
texture.SetLayerImage(0, 3, rightImage)
texture.SetLayerImage(0, 4, topImage)
texture.SetLayerImage(0, 5, bottomImage)
return texture, nil
},

// digest function
Expand All @@ -88,6 +82,44 @@ func CreateCubeTexture(cubeImageProvider Provider[*mdl.CubeImage], opts ...Opera
))
}

// CreateCubeMipmapTexture creates a new cube texture with the specified format
// and source mipmap images.
func CreateCubeMipmapTexture(cubeImagesProvider Provider[[]*mdl.CubeImage], opts ...Operation) Provider[*mdl.Texture] {
return OnceProvider(FuncProvider(
// get function
func() (*mdl.Texture, error) {
var cfg textureConfig
for _, opt := range opts {
if err := opt.Apply(&cfg); err != nil {
return nil, fmt.Errorf("failed to configure cube texture: %w", err)
}
}

cubeImages, err := cubeImagesProvider.Get()
if err != nil {
return nil, fmt.Errorf("failed to get cube image: %w", err)
}

dimension := cubeImages[0].Side(mdl.CubeSideFront).Width()
texture := mdl.CreateCubeTexture(dimension, len(cubeImages), cfg.format.ValueOrDefault(mdl.TextureFormatRGBA16F))
for i, cubeImage := range cubeImages {
texture.SetLayerImage(i, 0, cubeImage.Side(mdl.CubeSideFront))
texture.SetLayerImage(i, 1, cubeImage.Side(mdl.CubeSideRear))
texture.SetLayerImage(i, 2, cubeImage.Side(mdl.CubeSideLeft))
texture.SetLayerImage(i, 3, cubeImage.Side(mdl.CubeSideRight))
texture.SetLayerImage(i, 4, cubeImage.Side(mdl.CubeSideTop))
texture.SetLayerImage(i, 5, cubeImage.Side(mdl.CubeSideBottom))
}
return texture, nil
},

// digest function
func() ([]byte, error) {
return CreateDigest("create-cube-mipmap-texture", cubeImagesProvider, opts)
},
))
}

type textureConfig struct {
format opt.T[mdl.TextureFormat]
mipmapping bool
Expand Down
15 changes: 10 additions & 5 deletions game/asset/mdl/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -844,13 +844,18 @@ func (c *Converter) convertTexture(texture *Texture) (uint32, error) {
flags |= asset.TextureFlagMipmapping
}
assetTexture := asset.Texture{
Width: uint32(texture.Width()),
Height: uint32(texture.Height()),
Format: texture.Format(),
Flags: flags,
Layers: gog.Map(texture.layers, func(layer TextureLayer) asset.TextureLayer {
return asset.TextureLayer{
Data: layer.Data(),
MipmapLayers: gog.Map(texture.mipmapLayers, func(mipLayer MipmapLayer) asset.MipmapLayer {
return asset.MipmapLayer{
Width: uint32(mipLayer.Width()),
Height: uint32(mipLayer.Height()),
Depth: uint32(mipLayer.Depth()),
Layers: gog.Map(mipLayer.Layers(), func(layer TextureLayer) asset.TextureLayer {
return asset.TextureLayer{
Data: layer.Data(),
}
}),
}
}),
}
Expand Down
10 changes: 10 additions & 0 deletions game/asset/mdl/image_cube.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,13 @@ func (i *CubeImage) Scale(newSize int) *CubeImage {
}
return dstImage
}

func (i *CubeImage) MapTexels(fn func(texel Color) Color) *CubeImage {
dstImage := i.Scale(i.size) // copy
for _, sideImage := range dstImage.sides {
for i := range sideImage.texels {
sideImage.texels[i] = fn(sideImage.texels[i])
}
}
return dstImage
}
8 changes: 4 additions & 4 deletions game/asset/mdl/image_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,21 +141,21 @@ func BuildCubeSideFromEquirectangular(side CubeSide, srcImage *Image) *Image {
return dstImage
}

func BuildIrradianceCubeImage(srcImage *CubeImage, sampleCount int) *CubeImage {
func BuildIrradianceCubeImage(srcImage *CubeImage, sampleCount int, minDot float64) *CubeImage {
dstImage := NewCubeImage(srcImage.size)
var group sync.WaitGroup
for i := range srcImage.sides {
group.Add(1)
go func() {
defer group.Done()
projectIrradianceCubeImageSide(srcImage, dstImage, CubeSide(i), sampleCount)
projectIrradianceCubeImageSide(srcImage, dstImage, CubeSide(i), sampleCount, minDot)
}()
}
group.Wait()
return dstImage
}

func projectIrradianceCubeImageSide(srcImage, dstImage *CubeImage, side CubeSide, sampleCount int) {
func projectIrradianceCubeImageSide(srcImage, dstImage *CubeImage, side CubeSide, sampleCount int, minDot float64) {
dimension := srcImage.size

startLat := dprec.Degrees(-90.0)
Expand Down Expand Up @@ -195,7 +195,7 @@ func projectIrradianceCubeImageSide(srcImage, dstImage *CubeImage, side CubeSide
longitudeCS*latitudeCS,
latitudeSN,
)
if dot := dprec.Vec3Dot(uvw, direction); dot > 0.0 {
if dot := dprec.Vec3Dot(uvw, direction); dot > minDot {
positiveSamples++
srcColor := srcImage.TexelUVW(direction)
color.R += srcColor.R * dot
Expand Down
Loading

0 comments on commit 514aab3

Please sign in to comment.