sm64coopdx/docs/lua/examples/shader-demo/default-shader.lua

352 lines
No EOL
13 KiB
Lua

---@param item number
---@param with_alpha boolean
---@param only_alpha boolean
---@param hint_single_element boolean
---@return string
local function shader_item_to_str(item, with_alpha, only_alpha, hint_single_element)
if not only_alpha then
if item == SHADER_0 then
return with_alpha and "vec4(0.0, 0.0, 0.0, 0.0)" or "vec3(0.0, 0.0, 0.0)"
elseif item == SHADER_1 then
return with_alpha and "vec4(1.0, 1.0, 1.0, 1.0)" or "vec3(1.0, 1.0, 1.0)"
elseif item >= SHADER_INPUT_1 and item <= SHADER_INPUT_8 then
local idx = item - SHADER_INPUT_1 + 1
local name = "vInput" .. idx
return with_alpha and name or (name .. ".rgb")
elseif item == SHADER_TEXEL0 then
return with_alpha and "texVal0" or "texVal0.rgb"
elseif item == SHADER_TEXEL0A then
if hint_single_element then return "texVal0.a" end
return with_alpha and
"vec4(texVal0.a, texVal0.a, texVal0.a, texVal0.a)" or
"vec3(texVal0.a, texVal0.a, texVal0.a)"
elseif item == SHADER_TEXEL1 then
return with_alpha and "texVal1" or "texVal1.rgb"
elseif item == SHADER_TEXEL1A then
if hint_single_element then return "texVal1.a" end
return with_alpha and
"vec4(texVal1.a, texVal1.a, texVal1.a, texVal1.a)" or
"vec3(texVal1.a, texVal1.a, texVal1.a)"
elseif item == SHADER_COMBINED then
return with_alpha and "texel" or "texel.rgb"
elseif item == SHADER_COMBINEDA then
if hint_single_element then return "texel.a" end
return with_alpha and
"vec4(texel.a, texel.a, texel.a, texel.a)" or
"vec3(texel.a, texel.a, texel.a)"
elseif item == SHADER_NOISE then
return with_alpha and "vec4(noise)" or "vec3(noise)"
end
else
if item == SHADER_0 then
return "0.0"
elseif item == SHADER_1 then
return "1.0"
elseif item >= SHADER_INPUT_1 and item <= SHADER_INPUT_8 then
local idx = item - SHADER_INPUT_1 + 1
return "vInput" .. idx .. ".a"
elseif item == SHADER_TEXEL0 or item == SHADER_TEXEL0A then
return "texVal0.a"
elseif item == SHADER_TEXEL1 or item == SHADER_TEXEL1A then
return "texVal1.a"
elseif item == SHADER_COMBINED or item == SHADER_COMBINEDA then
return "texel.a"
elseif item == SHADER_NOISE then
return "noise.a"
end
end
return "unknown"
end
---@param cmd number[]
---@param do_single boolean
---@param do_multiply boolean
---@param do_mix boolean
---@param with_alpha boolean
---@param only_alpha boolean
---@return string
local function append_formula(cmd, do_single, do_multiply, do_mix, with_alpha, only_alpha)
local base = (only_alpha and 4 or 0)
if do_single then
return shader_item_to_str(cmd[base + 4], with_alpha, only_alpha, false)
elseif do_multiply then
local a = shader_item_to_str(cmd[base + 1], with_alpha, only_alpha, false)
local c = shader_item_to_str(cmd[base + 3], with_alpha, only_alpha, true)
return a .. " * " .. c
elseif do_mix then
local b = shader_item_to_str(cmd[base + 2], with_alpha, only_alpha, false)
local a = shader_item_to_str(cmd[base + 1], with_alpha, only_alpha, false)
local c = shader_item_to_str(cmd[base + 3], with_alpha, only_alpha, true)
return "mix(" .. b .. ", " .. a .. ", " .. c .. ")"
else
local a = shader_item_to_str(cmd[base + 1], with_alpha, only_alpha, false)
local b = shader_item_to_str(cmd[base + 2], with_alpha, only_alpha, false)
local c = shader_item_to_str(cmd[base + 3], with_alpha, only_alpha, true)
local d = shader_item_to_str(cmd[base + 4], with_alpha, only_alpha, false)
return "(" .. a .. " - " .. b .. ") * " .. c .. " + " .. d
end
end
---@param cc ColorCombiner
---@param shaderIndex integer
local function on_vertex_shader_create(cc, shaderIndex)
local opt_fog = cc.cm.flags & CM_FLAG_USE_FOG ~= 0
local opt_tex_persp = cc.cm.flags & CM_FLAG_TEX_PERSP ~= 0
local vs = {}
table.insert(vs, "#version 410 core")
table.insert(vs, "in vec4 aVtxPos;")
for t = 0, 1 do
table.insert(vs, string.format("in vec2 aTexCoord%d;", t))
if not opt_tex_persp then
table.insert(vs, string.format("noperspective out vec2 vTexCoord%d;", t))
else
table.insert(vs, string.format("out vec2 vTexCoord%d;", t))
end
end
table.insert(vs, "in vec2 aLightMap;")
table.insert(vs, "out vec2 vLightMap;")
for i = 1, CC_MAX_INPUTS do
table.insert(vs, string.format("in vec4 aInput%d;", i))
table.insert(vs, string.format("out vec4 vInput%d;", i))
end
table.insert(vs, "in vec3 aNormal;")
table.insert(vs, "out vec3 vNormal;")
table.insert(vs, "in vec3 aBarycentric;")
table.insert(vs, "out vec3 vBarycentric;")
if opt_fog then
table.insert(vs, "out float vFogZ;")
end
table.insert(vs, "uniform mat4 uModelViewProjectionMatrix;")
table.insert(vs, "uniform float uXAdjustRatio;")
table.insert(vs, "uniform float uFogMul;")
table.insert(vs, "uniform float uFogIntensity;")
table.insert(vs, "uniform float uFogOffset;")
table.insert(vs, "uniform float uDepthZSub;")
table.insert(vs, "uniform float uDepthZMult;")
table.insert(vs, "uniform float uDepthZAdd;")
table.insert(vs, "void main() {")
for t = 0, 1 do
table.insert(vs, string.format("vTexCoord%d = aTexCoord%d;", t, t))
end
table.insert(vs, "vLightMap = aLightMap;")
for i = 1, CC_MAX_INPUTS do
table.insert(vs, string.format("vInput%d = aInput%d;", i, i))
end
table.insert(vs, "vNormal = aNormal;")
table.insert(vs, "vBarycentric = aBarycentric;")
table.insert(vs, "vec4 clipPos = uModelViewProjectionMatrix * aVtxPos;")
table.insert(vs, "clipPos.x *= uXAdjustRatio;")
table.insert(vs, "gl_Position = clipPos;")
if opt_fog then
table.insert(vs, "float w = clipPos.w;")
table.insert(vs, "if (abs(w) < 0.001) w = 0.001;")
table.insert(vs, "float winv = 1.0 / w;")
table.insert(vs, "if (winv < 0.0) winv = 32767.0;")
table.insert(vs, "float adjClipZ = clipPos.z;")
table.insert(vs, "adjClipZ -= uDepthZSub;")
table.insert(vs, "adjClipZ *= uDepthZMult;")
table.insert(vs, "adjClipZ += uDepthZAdd;")
table.insert(vs, "float fog_z = (adjClipZ * winv * uFogMul * uFogIntensity) + uFogOffset;")
table.insert(vs, "vFogZ = clamp(fog_z / 255.0, 0.0, 1.0);")
end
table.insert(vs, "}")
return table.concat(vs, "\n")
end
---@param cc ColorCombiner
---@param shaderIndex integer
local function on_fragment_shader_create(cc, shaderIndex)
local ccf = gfx_color_combiner_get_features(cc)
local opt_alpha = cc.cm.flags & CM_FLAG_USE_ALPHA ~= 0
local opt_fog = cc.cm.flags & CM_FLAG_USE_FOG ~= 0
local opt_texture_edge = cc.cm.flags & CM_FLAG_TEXTURE_EDGE ~= 0
local opt_2cycle = cc.cm.flags & CM_FLAG_USE_2CYCLE ~= 0
local opt_light_map = cc.cm.flags & CM_FLAG_LIGHT_MAP ~= 0
local opt_dither = cc.cm.flags & CM_FLAG_USE_DITHER ~= 0
local world_geometry = cc.cm.flags & CM_FLAG_WORLD_GEOMETRY ~= 0
local opt_tex_persp = cc.cm.flags & CM_FLAG_TEX_PERSP ~= 0
local fs = {}
table.insert(fs, "#version 410 core")
table.insert(fs, "out vec4 fragColor;")
for t = 0, 1 do
if not opt_tex_persp then
table.insert(fs, string.format("noperspective in vec2 vTexCoord%d;", t))
else
table.insert(fs, string.format("in vec2 vTexCoord%d;", t))
end
end
table.insert(fs, "in vec2 vLightMap;")
for i = 1, CC_MAX_INPUTS do
table.insert(fs, string.format("in vec4 vInput%d;", i))
end
if opt_fog then
table.insert(fs, "in float vFogZ;")
end
if ccf.used_textures[1] then
table.insert(fs, "uniform sampler2D uTex0;")
table.insert(fs, "uniform vec2 uTex0Size;")
table.insert(fs, "uniform bool uTex0Filter;")
end
if ccf.used_textures[2] then
table.insert(fs, "uniform sampler2D uTex1;")
table.insert(fs, "uniform vec2 uTex1Size;")
table.insert(fs, "uniform bool uTex1Filter;")
end
-- 3-point filtering
if ccf.used_textures[1] or ccf.used_textures[2] then
table.insert(fs, "#define TEX_OFFSET(off) texture(tex, texCoord - (off)/texSize)")
table.insert(fs, "vec4 filter3point(in sampler2D tex, in vec2 texCoord, in vec2 texSize) {")
table.insert(fs, " vec2 offset = fract(texCoord * texSize - vec2(0.5));")
table.insert(fs, " offset -= step(1.0, offset.x + offset.y);")
table.insert(fs, " vec4 c0 = TEX_OFFSET(offset);")
table.insert(fs, " vec4 c1 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y));")
table.insert(fs, " vec4 c2 = TEX_OFFSET(vec2(offset.x, offset.y - sign(offset.y)));")
table.insert(fs, " return c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0);")
table.insert(fs, "}")
table.insert(fs, "vec4 sampleTex(in sampler2D tex, in vec2 uv, in vec2 texSize, in bool dofilter, in int filterType) {")
table.insert(fs, " if (dofilter && filterType == 2)")
table.insert(fs, " return filter3point(tex, uv, texSize);")
table.insert(fs, " return texture(tex, uv);")
table.insert(fs, "}")
end
if (opt_alpha and opt_dither) or ccf.do_noise then
table.insert(fs, "uniform float uFrameCount;")
table.insert(fs, "float random(in vec3 v) {")
table.insert(fs, " float r = dot(sin(v), vec3(12.9898, 78.233, 37.719));")
table.insert(fs, " return fract(sin(r) * 143758.5453);")
table.insert(fs, "}")
end
if opt_light_map then
table.insert(fs, "uniform vec3 uLightmapColor;")
end
if opt_fog then
table.insert(fs, "uniform vec3 uFogColor;")
end
table.insert(fs, "uniform int uFilter;")
table.insert(fs, "void main() {")
if (opt_alpha and opt_dither) or ccf.do_noise then
table.insert(fs, "float noise = floor(random(vec3(gl_FragCoord.xy, uFrameCount)) + 0.5);")
end
if ccf.used_textures[1] then
table.insert(fs, "vec4 texVal0 = sampleTex(uTex0, vTexCoord0, uTex0Size, uTex0Filter, uFilter);")
end
if ccf.used_textures[2] then
if opt_light_map then
table.insert(fs, "vec4 texVal1 = sampleTex(uTex1, vLightMap, uTex1Size, uTex1Filter, uFilter);")
table.insert(fs, "texVal0.rgb *= uLightmapColor.rgb;")
table.insert(fs, "texVal1.rgb = texVal1.rgb * texVal1.rgb + texVal1.rgb;")
else
table.insert(fs, "vec4 texVal1 = sampleTex(uTex1, vTexCoord1, uTex1Size, uTex1Filter, uFilter);")
end
end
if opt_alpha then
table.insert(fs, "vec4 texel = ")
else
table.insert(fs, "vec3 texel = ")
end
-- combine passes
for i = 0, (opt_2cycle and 1 or 0) do
local cmd = {}
for j = 0, CC_MAX_INPUTS - 1 do
cmd[j + 1] = cc.shader_commands[i * CC_MAX_INPUTS + j + 1]
end
local idx = i * 2
if not ccf.color_alpha_same[i + 1] and opt_alpha then
table.insert(fs, "vec4(")
table.insert(fs, append_formula(cmd, ccf.do_single[idx + 1], ccf.do_multiply[idx + 1], ccf.do_mix[idx + 1], false, false))
table.insert(fs, ", ")
table.insert(fs, append_formula(cmd, ccf.do_single[idx + 2], ccf.do_multiply[idx + 2], ccf.do_mix[idx + 2], true, true))
table.insert(fs, ")")
else
table.insert(fs,
append_formula(cmd,
ccf.do_single[idx + 1],
ccf.do_multiply[idx + 1],
ccf.do_mix[idx + 1],
opt_alpha,
false
)
)
end
table.insert(fs, ";")
if i == 0 and opt_2cycle then
table.insert(fs, "texel = ")
end
end
if opt_texture_edge and opt_alpha then
table.insert(fs, "if (texel.a > 0.3) texel.a = 1.0; else discard;")
end
if opt_fog then
if opt_alpha then
table.insert(fs, "texel = vec4(mix(texel.rgb, uFogColor, vFogZ), texel.a);")
else
table.insert(fs, "texel = mix(texel, uFogColor, vFogZ);")
end
end
if opt_alpha and opt_dither then
table.insert(fs, "texel.a *= noise;")
end
if opt_alpha then
table.insert(fs, "fragColor = texel;")
else
table.insert(fs, "fragColor = vec4(texel, 1.0);")
end
table.insert(fs, "}")
return table.concat(fs, "\n")
end
hook_event(HOOK_ON_VERTEX_SHADER_CREATE, on_vertex_shader_create)
hook_event(HOOK_ON_FRAGMENT_SHADER_CREATE, on_fragment_shader_create)