2k skybox tex; trilinear blending of atmos vars

This commit is contained in:
minjaesong
2023-08-07 13:57:58 +09:00
parent 30fb57eca3
commit 014306c209
6 changed files with 126 additions and 46 deletions

Binary file not shown.

View File

@@ -13,6 +13,9 @@ import net.torvald.unicode.EMDASH
import net.torvald.colourutil.* import net.torvald.colourutil.*
import net.torvald.parametricsky.datasets.DatasetCIEXYZ import net.torvald.parametricsky.datasets.DatasetCIEXYZ
import net.torvald.terrarum.abs import net.torvald.terrarum.abs
import net.torvald.terrarum.clut.Skybox
import net.torvald.terrarum.clut.Skybox.coerceInSmoothly
import net.torvald.terrarum.clut.Skybox.mapCircle
import net.torvald.terrarum.inUse import net.torvald.terrarum.inUse
import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI
import java.awt.BorderLayout import java.awt.BorderLayout
@@ -131,7 +134,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
} }
val outTexWidth = 1 val outTexWidth = 1
val outTexHeight = 256 val outTexHeight = 128
private fun Float.scaleFun() = private fun Float.scaleFun() =
(1f - 1f / 2f.pow(this/6f)) * 0.97f (1f - 1f / 2f.pow(this/6f)) * 0.97f
@@ -185,6 +188,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
val ys2 = ArrayList<Float>() val ys2 = ArrayList<Float>()
val halfHeight = oneScreen.height * 0.5 val halfHeight = oneScreen.height * 0.5
val elevationDeg = Math.toDegrees(elevation)
for (x in 0 until oneScreen.width) { for (x in 0 until oneScreen.width) {
for (y in 0 until oneScreen.height) { for (y in 0 until oneScreen.height) {
@@ -196,17 +200,19 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
val theta = sqrt(xf*xf + yf*yf) * HALF_PI*/ val theta = sqrt(xf*xf + yf*yf) * HALF_PI*/
// AM-PM mapping (use with WIDTH=1) // AM-PM mapping (use with WIDTH=1)
var yf = (y * 2.0 / oneScreen.height) % 1.0 val yp = y % (oneScreen.height / 2)
if (elevation < 0) yf *= 1.0 - pow(-elevation / HALF_PI, 0.333) val yi = yp - 3
val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)
if (elevationDeg < 0) yf *= Skybox.superellipsoidDecay(1.0 / 3.0, xf)
val theta = yf * HALF_PI
val gamma = if (y < halfHeight) HALF_PI else 3 * HALF_PI val gamma = if (y < halfHeight) HALF_PI else 3 * HALF_PI
val theta = yf.mapCircle() * HALF_PI
val xyz = CIEXYZ( val xyz = CIEXYZ(
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 0).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 2).toFloat() ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 2).toFloat(),
) )
val xyz2 = xyz.scaleToFit(elevation) val xyz2 = xyz.scaleToFit(elevation)
ys.add(xyz.Y) ys.add(xyz.Y)
@@ -214,12 +220,12 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
val rgb = xyz2.toRGB().toColor() val rgb = xyz2.toRGB().toColor()
rgb.a = 1f rgb.a = 1f
val rgb2 = Color( /*val rgb2 = Color(
((rgb.r * 255f).roundToInt() xor 0xAA) / 255f, ((rgb.r * 255f).roundToInt() xor 0xAA) / 255f,
((rgb.g * 255f).roundToInt() xor 0xAA) / 255f, ((rgb.g * 255f).roundToInt() xor 0xAA) / 255f,
((rgb.b * 255f).roundToInt() xor 0xAA) / 255f, ((rgb.b * 255f).roundToInt() xor 0xAA) / 255f,
rgb.a rgb.a
) )*/
oneScreen.setColor(rgb) oneScreen.setColor(rgb)
oneScreen.drawPixel(x, y) oneScreen.drawPixel(x, y)

View File

@@ -60,7 +60,7 @@ fun main() {
ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs()) ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs())
for (yp in 0 until Skybox.gradSize) { for (yp in 0 until Skybox.gradSize) {
val yi = yp - 3 val yi = yp - 10
val xf = -elevationDeg / 90.0 val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95) var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)

View File

@@ -13,6 +13,7 @@ import net.torvald.parametricsky.ArHosekSkyModel
import net.torvald.terrarum.App import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.abs import net.torvald.terrarum.abs
import net.torvald.terrarum.floorToInt
import net.torvald.terrarum.toInt import net.torvald.terrarum.toInt
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.* import kotlin.math.*
@@ -26,7 +27,7 @@ object Skybox : Disposable {
private const val PI = 3.141592653589793 private const val PI = 3.141592653589793
private const val TWO_PI = 6.283185307179586 private const val TWO_PI = 6.283185307179586
const val gradSize = 64 const val gradSize = 78
private lateinit var gradTexBinLowAlbedo: Array<TextureRegion> private lateinit var gradTexBinLowAlbedo: Array<TextureRegion>
private lateinit var gradTexBinHighAlbedo: Array<TextureRegion> private lateinit var gradTexBinHighAlbedo: Array<TextureRegion>
@@ -38,6 +39,7 @@ object Skybox : Disposable {
fun loadlut() { fun loadlut() {
tex = Texture(Gdx.files.internal("assets/clut/skybox.png")) tex = Texture(Gdx.files.internal("assets/clut/skybox.png"))
tex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear) tex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
tex.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat)
texRegions = TextureRegionPack(tex, 2, gradSize - 2, 0, 2, 0, 1) texRegions = TextureRegionPack(tex, 2, gradSize - 2, 0, 2, 0, 1)
texStripRegions = TextureRegionPack(tex, elevCnt, gradSize - 2, 0, 2, 0, 1) texStripRegions = TextureRegionPack(tex, elevCnt, gradSize - 2, 0, 2, 0, 1)
} }
@@ -55,20 +57,63 @@ object Skybox : Disposable {
TODO() TODO()
} }
fun getUV(elevationDeg: Double, turbidity: Double, albedo: Double): Pair<Texture, FloatArray> { data class SkyboxRenderInfo(
val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(turbDivisor).roundToInt() val texture: Texture,
val alb = albedo.coerceIn(0.0, 1.0).times(turbDivisor).roundToInt() val uvs: FloatArray,
val region1 = texStripRegions.get(alb + albedoCnt * 0, turb) // left half of the sheet val turbidityPoint: Float,
val region2 = texStripRegions.get(alb + albedoCnt * 1, turb) // right half of the sheet val albedoPoint: Float,
)
fun getUV(elevationDeg: Double, turbidity: Double, albedo: Double): SkyboxRenderInfo {
val turb = turbidity.coerceIn(turbiditiesD.first(), turbiditiesD.last()).minus(1.0).times(turbDivisor)
val turbLo = turb.floorToInt()
val turbHi = min(turbCnt - 1, turbLo + 1)
val alb = albedo.coerceIn(albedos.first(), albedos.last()).times(5.0)
val albLo = alb.floorToInt()
val albHi = min(albedoCnt - 1, albLo + 1)
val elev = elevationDeg.coerceIn(-elevMax, elevMax).plus(elevMax).div(elevations.last.toDouble()).div(albedoCnt * 2).times((elevCnt - 1.0) / elevCnt) val elev = elevationDeg.coerceIn(-elevMax, elevMax).plus(elevMax).div(elevations.last.toDouble()).div(albedoCnt * 2).times((elevCnt - 1.0) / elevCnt)
val uA = region1.u + (0.5f / tex.width) + elev.toFloat() // because of the nature of bilinear interpolation, half pixels from the edges must be discarded // A: morn, turbLow, albLow
val uB = region2.u + (0.5f / tex.width) + elev.toFloat() // because of the nature of bilinear interpolation, half pixels from the edges must be discarded // B: noon, turbLow, albLow
// C: morn, turbHigh, albLow
// D: noon, turbHigh, albLow
// E: morn, turbLow, albHigh
// F: noon, turbLow, albHigh
// G: morn, turbHigh, albHigh
// H: noon, turbHigh, albHigh
return tex to floatArrayOf( val regionA = texStripRegions.get(albLo + albedoCnt * 0, turbLo)
uA, region1.v, uA, region1.v2, val regionB = texStripRegions.get(albLo + albedoCnt * 1, turbLo)
uB, region2.v, uB, region2.v2, val regionC = texStripRegions.get(albLo + albedoCnt * 0, turbHi)
val regionD = texStripRegions.get(albLo + albedoCnt * 1, turbHi)
val regionE = texStripRegions.get(albHi + albedoCnt * 0, turbLo)
val regionF = texStripRegions.get(albHi + albedoCnt * 1, turbLo)
val regionG = texStripRegions.get(albHi + albedoCnt * 0, turbHi)
val regionH = texStripRegions.get(albHi + albedoCnt * 1, turbHi)
// (0.5f / tex.width): because of the nature of bilinear interpolation, half pixels from the edges must be discarded
val uA = regionA.u + (0.5f / tex.width) + elev.toFloat()
val uB = regionB.u + (0.5f / tex.width) + elev.toFloat()
val uC = regionC.u + (0.5f / tex.width) + elev.toFloat()
val uD = regionD.u + (0.5f / tex.width) + elev.toFloat()
val uE = regionE.u + (0.5f / tex.width) + elev.toFloat()
val uF = regionF.u + (0.5f / tex.width) + elev.toFloat()
val uG = regionG.u + (0.5f / tex.width) + elev.toFloat()
val uH = regionH.u + (0.5f / tex.width) + elev.toFloat()
return SkyboxRenderInfo(
tex,
floatArrayOf(
uA, regionA.v, uA, regionA.v2,
uB, regionB.v, uB, regionB.v2,
uC, regionC.v, uC, regionC.v2,
uD, regionD.v, uD, regionD.v2,
uE, regionE.v, uE, regionE.v2,
uF, regionF.v, uF, regionF.v2,
uG, regionG.v, uG, regionG.v2,
uH, regionH.v, uH, regionH.v2,
),
(turb - turbLo).toFloat(),
(alb - albLo).toFloat(),
) )
} }
@@ -107,15 +152,13 @@ object Skybox : Disposable {
val elevations = (0..150) val elevations = (0..150)
val elevMax = elevations.last / 2.0 val elevMax = elevations.last / 2.0
val elevationsD = elevations.map { -elevMax + it } // -75, -74, -73, ..., 74, 75 // (specifically using whole number of angles because angle units any finer than 1.0 would make "hack" sunsut happen too fast) val elevationsD = elevations.map { -elevMax + it } // -75, -74, -73, ..., 74, 75 // (specifically using whole number of angles because angle units any finer than 1.0 would make "hack" sunsut happen too fast)
val turbidities = (0..45) // 1, 1.2, 1.4, 1.6, ..., 10.0 val turbidities = (0..25) // 1, 1.2, 1.4, 1.6, ..., 6.0
val turbDivisor = 5.0 val turbDivisor = 5.0
val turbiditiesD = turbidities.map { 1.0 + it / turbDivisor } val turbiditiesD = turbidities.map { 1.0 + it / turbDivisor }
val albedos = arrayOf(0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0) val albedos = arrayOf(0.0, 0.2, 0.4, 0.6, 0.8, 1.0)
val elevCnt = elevations.count() val elevCnt = elevations.count()
val turbCnt = turbidities.count() val turbCnt = turbidities.count()
val albedoCnt = albedos.size val albedoCnt = albedos.size
val albedoLow = 0.1
val albedoHight = 0.8 // for theoretical "winter wonderland"?
val gamma = HALF_PI val gamma = HALF_PI
internal fun Double.mapCircle() = sin(HALF_PI * this) internal fun Double.mapCircle() = sin(HALF_PI * this)
@@ -123,8 +166,7 @@ object Skybox : Disposable {
internal fun initiate() { internal fun initiate() {
printdbg(this, "Initialising skybox model") printdbg(this, "Initialising skybox model")
gradTexBinLowAlbedo = getTexturmaps(albedoLow) TODO()
gradTexBinHighAlbedo = getTexturmaps(albedoHight)
App.disposables.add(this) App.disposables.add(this)
@@ -200,7 +242,7 @@ object Skybox : Disposable {
// printdbg(this, "elev $elevationDeg turb $turbidity") // printdbg(this, "elev $elevationDeg turb $turbidity")
for (yp in 0 until gradSize) { for (yp in 0 until gradSize) {
val yi = yp - 3 val yi = yp - 10
val xf = -elevationDeg / 90.0 val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95) var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)

View File

@@ -154,7 +154,7 @@ internal object WeatherMixer : RNGConsumer {
} }
var turbidity = 4.0; private set var turbidity = 1.0; private set
private var gH = 1.4f * App.scr.height private var gH = 1.4f * App.scr.height
// private var gH = 0.8f * App.scr.height // private var gH = 0.8f * App.scr.height
@@ -168,9 +168,11 @@ internal object WeatherMixer : RNGConsumer {
drawSkybox(camera, batch, world) drawSkybox(camera, batch, world)
} }
private val parallaxDomainSize = 400f
private val turbidityDomainSize = 533.3333f
private fun drawSkybox(camera: Camera, batch: FlippingSpriteBatch, world: GameWorld) { private fun drawSkybox(camera: Camera, batch: FlippingSpriteBatch, world: GameWorld) {
val parallaxZeroPos = (world.height / 3f) val parallaxZeroPos = (world.height / 3f)
val parallaxDomainSize = 300f
// we will not care for nextSkybox for now // we will not care for nextSkybox for now
val timeNow = (forceTimeAt ?: world.worldTime.TIME_T.toInt()) % WorldTime.DAY_LENGTH val timeNow = (forceTimeAt ?: world.worldTime.TIME_T.toInt()) % WorldTime.DAY_LENGTH
@@ -199,19 +201,20 @@ internal object WeatherMixer : RNGConsumer {
-+ <- 0.0 = -+ <- 0.0 =
*/ */
val parallax = ((parallaxZeroPos - WorldCamera.gdxCamY.div(TILE_SIZEF)) / parallaxDomainSize).times(-1f).coerceIn(-1f, 1f) val parallax = ((parallaxZeroPos - WorldCamera.gdxCamY.div(TILE_SIZEF)) / parallaxDomainSize).times(-1f).coerceIn(-1f, 1f)
val turbidityCoeff = ((parallaxZeroPos - WorldCamera.gdxCamY.div(TILE_SIZEF)) / turbidityDomainSize).times(-1f).coerceIn(-1f, 1f)
parallaxPos = parallax parallaxPos = parallax
// println(parallax) // parallax value works as intended. // println(parallax) // parallax value works as intended.
gdxBlendNormalStraightAlpha() gdxBlendNormalStraightAlpha()
turbidity = (3.5 + turbidityCoeff * 2.5).coerceIn(1.0, 6.0)
val thisTurbidity = forceTurbidity ?: turbidity val thisTurbidity = forceTurbidity ?: turbidity
// TODO trilinear with (deg, turb, alb)
val gradY = -(gH - App.scr.height) * ((parallax + 1f) / 2f) val gradY = -(gH - App.scr.height) * ((parallax + 1f) / 2f)
val (tex, uvs) = Skybox.getUV(solarElev, thisTurbidity, 0.3) val (tex, uvs, turbX, albX) = Skybox.getUV(solarElev, thisTurbidity, 0.3)
val texBlend = (1f/4000f * (timeNow - 43200) + 0.5f).coerceIn(0f, 1f) // 0.0 at T41200; 0.5 at T43200; 1.0 at T45200; val mornNoonBlend = (1f/4000f * (timeNow - 43200) + 0.5f).coerceIn(0f, 1f) // 0.0 at T41200; 0.5 at T43200; 1.0 at T45200;
starmapTex.texture.bind(1) starmapTex.texture.bind(1)
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it
@@ -223,9 +226,15 @@ internal object WeatherMixer : RNGConsumer {
batch.shader = shaderBlendMax batch.shader = shaderBlendMax
shaderBlendMax.setUniformi("tex1", 1) shaderBlendMax.setUniformi("tex1", 1)
shaderBlendMax.setUniformf("drawOffsetSize", 0f, gradY, App.scr.wf, gH) shaderBlendMax.setUniformf("drawOffsetSize", 0f, gradY, App.scr.wf, gH)
shaderBlendMax.setUniform4fv("skyboxUV1", uvs, 0, 4) shaderBlendMax.setUniform4fv("uvA", uvs, 0, 4)
shaderBlendMax.setUniform4fv("skyboxUV2", uvs, 4, 4) shaderBlendMax.setUniform4fv("uvB", uvs, 4, 4)
shaderBlendMax.setUniformf("texBlend", texBlend) shaderBlendMax.setUniform4fv("uvC", uvs, 8, 4)
shaderBlendMax.setUniform4fv("uvD", uvs, 12, 4)
shaderBlendMax.setUniform4fv("uvE", uvs, 16, 4)
shaderBlendMax.setUniform4fv("uvF", uvs, 20, 4)
shaderBlendMax.setUniform4fv("uvG", uvs, 24, 4)
shaderBlendMax.setUniform4fv("uvH", uvs, 28, 4)
shaderBlendMax.setUniformf("texBlend", mornNoonBlend, turbX, albX, 0f)
shaderBlendMax.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY) shaderBlendMax.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY)
shaderBlendMax.setUniformf("randomNumber", shaderBlendMax.setUniformf("randomNumber",
// (world.worldTime.TIME_T.plus(31L) xor 1453L + 31L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(31L) xor 1453L + 31L).and(1023).toFloat(),

View File

@@ -14,9 +14,15 @@ out vec4 fragColor;
const vec2 boolean = vec2(0.0, 1.0); const vec2 boolean = vec2(0.0, 1.0);
uniform vec4 drawOffsetSize; // (gradX, gradY, gradW, gradH) uniform vec4 drawOffsetSize; // (gradX, gradY, gradW, gradH)
uniform vec4 skyboxUV1; // (u, v, u2, v2) for the skybox drawing (morning) uniform vec4 uvA; // (u, v, u2, v2) for morn, turbLow, albLow
uniform vec4 skyboxUV2; // (u, v, u2, v2) for the skybox drawing (afternoon) uniform vec4 uvB; // (u, v, u2, v2) for noon, turbLow, albLow
uniform float texBlend; uniform vec4 uvC; // (u, v, u2, v2) for morn, turbHigh, albLow
uniform vec4 uvD; // (u, v, u2, v2) for noon, turbHigh, albLow
uniform vec4 uvE; // (u, v, u2, v2) for morn, turbLow, albHigh
uniform vec4 uvF; // (u, v, u2, v2) for noon, turbLow, albHigh
uniform vec4 uvG; // (u, v, u2, v2) for morn, turbHigh, albHigh
uniform vec4 uvH; // (u, v, u2, v2) for noon, turbHigh, albHigh
uniform vec4 texBlend; // (morn/noon, turbidity, albedo, unused)
uniform vec2 tex1Size = vec2(4096.0); uniform vec2 tex1Size = vec2(4096.0);
uniform vec2 astrumScroll = vec2(0.0); uniform vec2 astrumScroll = vec2(0.0);
uniform vec4 randomNumber = vec4(1.0, -2.0, 3.0, -4.0); uniform vec4 randomNumber = vec4(1.0, -2.0, 3.0, -4.0);
@@ -112,19 +118,36 @@ vec4 random(vec2 p) {
// draw call to this function must use UV coord of (0,0,1,1)! // draw call to this function must use UV coord of (0,0,1,1)!
void main(void) { void main(void) {
vec2 skyboxTexCoordMorning = mix(skyboxUV1.xy, skyboxUV1.zw, v_texCoords); vec4 colorTexA = texture(u_texture, mix(uvA.xy, uvA.zw, v_texCoords));
vec2 skyboxTexCoordAfternoon = mix(skyboxUV2.xy, skyboxUV2.zw, v_texCoords); vec4 colorTexB = texture(u_texture, mix(uvB.xy, uvB.zw, v_texCoords));
vec4 colorTexC = texture(u_texture, mix(uvC.xy, uvC.zw, v_texCoords));
vec4 colorTexD = texture(u_texture, mix(uvD.xy, uvD.zw, v_texCoords));
vec4 colorTexE = texture(u_texture, mix(uvE.xy, uvE.zw, v_texCoords));
vec4 colorTexF = texture(u_texture, mix(uvF.xy, uvF.zw, v_texCoords));
vec4 colorTexG = texture(u_texture, mix(uvG.xy, uvG.zw, v_texCoords));
vec4 colorTexH = texture(u_texture, mix(uvH.xy, uvH.zw, v_texCoords));
vec2 astrumTexCoord = (v_texCoords * drawOffsetSize.zw + drawOffsetSize.xy + astrumScroll) / tex1Size; vec2 astrumTexCoord = (v_texCoords * drawOffsetSize.zw + drawOffsetSize.xy + astrumScroll) / tex1Size;
vec4 randomness = snoise4((gl_FragCoord.xy - astrumScroll) * 0.16) * 2.0; // multiply by 2 so that the "density" of the stars would be same as the non-random version vec4 randomness = snoise4((gl_FragCoord.xy - astrumScroll) * 0.16) * 2.0; // multiply by 2 so that the "density" of the stars would be same as the non-random version
vec4 colorTex0Morining = texture(u_texture, skyboxTexCoordMorning);
vec4 colorTex0Afternoon = texture(u_texture, skyboxTexCoordAfternoon);
vec4 colorTex0 = mix(colorTex0Morining, colorTex0Afternoon, texBlend);
vec4 colorTex1 = texture(tex1, astrumTexCoord) * randomness; vec4 colorTex1 = texture(tex1, astrumTexCoord) * randomness;
// notations used: https://en.wikipedia.org/wiki/File:Enclosing_points.svg and https://en.wikipedia.org/wiki/File:3D_interpolation2.svg
vec4 colorTex0 = mix(
mix(
mix(colorTexA, colorTexE, texBlend.z), // c00 = c000..c100
mix(colorTexC, colorTexG, texBlend.z), // c10 = c010..c110
texBlend.y
), // c0 = c00..c10
mix(
mix(colorTexB, colorTexF, texBlend.z), // c01 = c001..c101
mix(colorTexD, colorTexH, texBlend.z), // c11 = c011..c111
texBlend.y
), // c1 = c01..c11
texBlend.x
); // c = c0..c1
fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + boolean.xxxy; fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + boolean.xxxy;