Shader: kesan cahaya
Ujian sama ada shader berfungsi
Umum
Walaupun saya memberi permainan ini rupa retro tertentu, saya tetap mahu menggunakan shader.
Dalam video di bawah anda boleh lihat saya bereksperimen dengan kesan cahaya. Jika anda perhatikan, saya berjalan menembusi cahaya dan pemain juga terkesan oleh kesan tersebut.
Termasuk shader yang bergerak, contohnya untuk obor yang akan kita perlukan dalam pengembaraan ini.
Reka bentuk
Jika saya perlu menggambarkannya sebagai rajah, lebih kurang begini, cuma malangnya ia jauh lebih rumit untuk dibuat.

Rajah shader
Pengalaman menunjukkan bahawa dalam love2d tidak boleh merender beberapa shader dalam draw loop yang sama. Ia tidak boleh dilakukan, dan saya hanya sedar selepas satu shader berjaya berfungsi dan saya mahu mengaktifkan yang kedua.
shader1 = love.graphics.newShader('color-dodge.glsl')
shader2 = love.graphics.newShader('color-dodge.glsl')
function love.draw()
love.graphics.push("all")
love.graphics.setCanvas(src)
love.graphics.clear()
love.graphics.setShader()
love.graphics.draw(scene, 0, 0)
love.graphics.draw(player, playerPos.x, playerPos.y)
love.graphics.setShader(shader1)
love.graphics.setShader(shader2)
love.graphics.setCanvas()
love.graphics.setShader()
love.graphics.draw(src, 0, 0)
end
Nasib baik ada penyelesaian untuk masalah ini, iaitu rendering multi-pass. Maksudnya, hasil yang anda lukis pada canvas ditulis semula ke canvas lain, kemudian shader dilukis. Ini boleh diulang untuk beberapa shader.
Fungsi di bawah berfungsi dengan baik buat masa ini, walaupun saya masih perlu mengujinya dengan lebih teliti.
function Engine3:applyIntermediateShader(shader)
if not shader then return end
local src = self.canvas:getCanvas()
local front = self.canvas:getCanvasFront()
-- Store current graphics state
local currentCanvas = love.graphics.getCanvas()
local currentShader = love.graphics.getShader()
-- Apply shader: src -> front
love.graphics.push("all")
love.graphics.origin()
love.graphics.setCanvas(front)
love.graphics.clear()
love.graphics.setShader(shader:getShader())
love.graphics.draw(src, 0, 0)
love.graphics.setShader()
love.graphics.pop()
-- Copy result back to main canvas: front -> src
love.graphics.push("all")
love.graphics.origin()
love.graphics.setCanvas(src)
love.graphics.clear()
love.graphics.draw(front, 0, 0)
love.graphics.pop()
-- Restore graphics state for continued rendering
love.graphics.setCanvas(currentCanvas)
love.graphics.setShader(currentShader)
end
Shader Color Dodge
Dalam reka bentuk Photoshop dan Aseprite saya menggunakan penapis color dodge.

Color Dodge dalam Photoshop
Jadi dalam permainan dan editor saya perlu meniru kesan ini dengan satu cara.

Pemilih shader dalam editor
Saya hampir tidak menggunakan AI untuk memprogram semasa membangunkan permainan ini. Tetapi kadangkala saya tahu ia bahagian kecil yang saya belum ada pengetahuan dan boleh digunakan sebagai titik permulaan. Jadi saya meminta untuk membuat shader glsl bernama ‘Color Dodge’ yang sama seperti yang dilakukan Photoshop.
Sudah tentu ia tidak berfungsi dengan baik, kerana koordinat yang saya gunakan berbeza dan saya juga menggunakan penskalaan dan perkara lain. Fail glsl akhir buat masa ini ialah seperti ini.
extern vec2 position;
extern Image image;
extern float scale;
extern float overlayIntensity;
extern vec4 quadRect;
vec3 colorDodge(vec3 base, vec3 blend) {
return vec3(
blend.r >= 0.999 ? 1.0 : min(1.0, base.r / max(0.001, 1.0 - blend.r)),
blend.g >= 0.999 ? 1.0 : min(1.0, base.g / max(0.001, 1.0 - blend.g)),
blend.b >= 0.999 ? 1.0 : min(1.0, base.b / max(0.001, 1.0 - blend.b))
);
}
vec4 effect(vec4 color, Image tex, vec2 tc, vec2 sc)
{
vec4 base = texture(tex, tc);
vec2 texSize = vec2(textureSize(image, 0));
vec2 qpos = quadRect.xy;
vec2 qsize = quadRect.zw;
vec2 uv = (sc - position) / (qsize * scale);
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
return base;
}
uv = (uv * qsize + qpos) / texSize;
vec4 overlay = texture(image, uv);
if (overlay.a > 0.0) {
vec3 enhancedOverlay = min(vec3(1.0), overlay.rgb * overlayIntensity);
vec3 dodged = colorDodge(base.rgb, enhancedOverlay);
float enhancedAlpha = min(1.0, overlay.a * overlayIntensity);
vec3 result = mix(base.rgb, dodged, enhancedAlpha);
return vec4(result, base.a);
}
return base;
}
Dalam kelas shader saya menghantar Frameset dan kesan yang dipilih kepada shader.
if frameset then
self:setImage(frameset.resource)
self:setScale(nil, true)
self:setPosition(frameset:getFrameX(), frameset:getFrameY(), true)
if frameset:getQuad() then
self:setQuad(frameset:getQuad():getViewport())
end
end
Akhir kata
Saya berjaya membawa color dodge daripada Photoshop ke dalam permainan, tetapi masih perlu mencuba perkara lain seperti bayang-bayang dan gabungan sistem zarah untuk api dengan kesan shader.
Namun saya rasa ia perlu untuk menjadikan semuanya lebih cantik. Nasib baik penapis glsl juga boleh didapati di pelbagai laman web, jadi saya tidak perlu mereka semuanya dari awal.