用 Love2D 實現法線貼圖的例程(到最新版本 0.10.1)


用 Love2D 實現法線貼圖的例程(到最新版本 0.10.1)

概述

一般來說, 復雜的光照模型會被用在 3D 游戲中, 以產生逼真的效果, 不過也有些開發者研究出一些代碼可以在 2D 游戲中使用這些光照模型, 這里就有兩個在 2D 場景中使用法線貼圖的 Love2D 例程, 不過是前幾年寫的, 用的是 Love2D 的舊版本, 到了今天最新版本的 Love2D 的很多函數都發生了變化, 本文的目標就是修改這些函數到最新的 Love2D 版本 0.10.1.

例程1

這是一個用來演示 2D 場景下使用光照模型的例程, 原始下載鏈接 PixelArtCelShading.love

對 .love 文件解包

OSX 下很簡單, 先新建一個目錄 shader1, 接着把 PixelArtCelShading.love 文件拷貝進去, 然后執行 unzip 命令, 就可以得到這個 love2d 項目的全部源文件了, 因為我們有兩個例程, 所以還要新建一個目錄 shader2, 把 PixelCelShadingAmbientOcclusion2.love 拷貝進去, 如下:

Air:lua admin$ mv PixelCelShadingAmbientOcclusion2.love ./shader2
Air:lua admin$ cd shader2
Air:shader2 admin$ ls
PixelCelShadingAmbientOcclusion2.love
Air:shader2 admin$ unzip ./ PixelCelShadingAmbientOcclusion2.love 
unzip: cannot find or open ./, ./.zip or ./.ZIP.
Air:shader2 admin$ unzip ./PixelCelShadingAmbientOcclusion2.love 
Archive: ./PixelCelShadingAmbientOcclusion2.love
inflating: globe.png 
inflating: main.lua 
extracting: pithos.png 
inflating: pixel_font.png 
inflating: shader.glsl 
Air:shader2 admin$ ls
PixelCelShadingAmbientOcclusion2.love	main.lua	pixel_font.png
globe.png	pithos.png	shader.glsl
Air:shader2 admin$

具體修改

love 命令加載, 出現了如下錯誤:

Air:shader2 admin$ love ../shader1
Error: main.lua:25: attempt to call field 'setDefaultImageFilter' (a nil value)
stack traceback:
main.lua:25: in function 'load'
[string "boot.lua"]:439: in function <[string "boot.lua"]:435>
[C]: in function 'xpcall'
Air:shader2 admin$ love ../shader1
Error: main.lua:31: attempt to call field 'newPixelEffect' (a nil value)
stack traceback:
main.lua:31: in function 'load'
[string "boot.lua"]:439: in function <[string "boot.lua"]:435>
[C]: in function 'xpcall'
Air:shader2 admin$ love ../shader1
Error: main.lua:50: attempt to call method 'clear' (a nil value)
stack traceback:
main.lua:50: in function 'draw'
[string "boot.lua"]:467: in function <[string "boot.lua"]:435>
[C]: in function 'xpcall'
Air:shader2 admin$

修改方法也很簡單, 打開 Love2D官網Wiki文檔, 查看 love.graphics 模塊的函數 setDefaultImageFilter, 發現在版本 0.10.0 之后就改名為 setDefaultFilter 了, 后面的 newPixelEffect 也被改為 newShader``, 順手把 setPixelEffect 也改成 setShader.

關於 clear() 方法稍微不同, 因為它是 Canvas 對象的一個方法, 查詢 Canvas, 發現它的 clear() 方法被 love.graphics.clear() 取代了, 直接改過去, 發現不起作用, 會出現很多拖影, 如下圖:

說明沒起作用, 再仔細閱讀一遍文檔, 發現要跟 love.graphics.setCanvas(fb) 配合使用, 也就是說要把這條清除語句放在 love.graphics.setCanvas(fb) 語句后面, 修改順序為:

G.setCanvas(fb)
G.clear(0,0,0,0)

果然起作用了, 拖影被消掉了, 如下圖:

更新后代碼

修改后的 main.lua 文件代碼如下:

local function captionf (...)
G.setCaption(string.format(...))
end

local function distance (x1, y1, x2, y2)
return ((x1 - x2)^2 + (y1 - y2)^2)^0.5
end

local sz = 3
local z = 30
local function update_light_vector ()
local x, y = love.mouse.getPosition()
y = 600 - y -- glsl works from bottom left rather than top left
x = x/sz
y = y/sz
mouse = {x=x, y=600/sz-y}
effect:send("light_vec", {x, y, z})
end

function love.load ()
G = love.graphics
G.setDefaultFilter("nearest", "nearest")
G.setBackgroundColor(35, 30, 65)
stump = G.newImage "treestump.png"
stump_lines = G.newImage "treestump_lines.png"
stump_diffuse = G.newImage "treestump_diffuse.png"
globe = G.newImage "globe.png"
effect = G.newShader "gooch.glsl"
G.setShader(effect)
update_light_vector()
fb = G.newCanvas(800/sz, 600/sz)
fb:setFilter("nearest", "nearest")
effect:send("diffuse", stump_diffuse)
end

time = 0
function love.update (dt)
update_light_vector()

time = time+dt
z = z + math.cos(time)/3
end

local r = math.random
function love.draw ()
G.setColor(255, 255, 255, 255)

G.setCanvas(fb)
G.clear(0,0,0,0)

math.randomseed(2)
G.setShader(effect)
for x = 20, (800-34)/sz, 34 do
for y = 20, (600-34)/sz, 34 do
if r() > 0.7 then
G.draw(stump, x, y, 0, 1, 1, 16, 16)
G.setShader()
local q = 0.3
G.setColor(145*q, 75*q, 39*q)
G.draw(stump_lines, x, y, 0, 1, 1, 16, 16)
G.setShader(effect)
end
end
end
G.setShader()
G.setColor(255, 255, 255)
G.draw(globe, mouse.x, mouse.y-z, 0, 1, 1, 8, 8)
G.setCanvas()
G.draw(fb, 0, 0, 0, sz, sz)
end

打包

如果想打包成 .love 的形式, 以便發布, 可以用命令 zip -0 -r -X -q, 操作紀錄如下:

Air:lua admin$ cd shader1
Air:shader1 admin$ zip -0 -r -X -q ../shader1.love ./*
Air:shader1 admin$ cd ..
Air:lua admin$ ls -al
total 152
drwxr-xr-x 18 admin staff 612 7 5 15:13 .
drwxr-xr-x 15 admin staff 510 7 4 09:55 ..
-rw-r--r--@ 1 admin staff 12292 7 5 14:41 .DS_Store
drwxr-xr-x 6 admin staff 204 7 1 21:11 2048
-rw-r--r--@ 1 admin staff 7691 7 5 10:05 Pixel1
-rw-r--r--@ 1 admin staff 7691 9 27 2012 PixelArtCelShading.love
-rw-r--r--@ 1 admin staff 10471 9 27 2012 PixelCelShadingAmbientOcclusion2.love
drwxr-xr-x 6 admin staff 204 7 4 00:01 c-test
drwxr-xr-x 5 admin staff 170 7 1 21:12 love
drwxr-xr-x 6 admin staff 204 7 1 21:06 particle
drwxr-xr-x 8 admin staff 272 7 5 15:05 shader1
-rw-r--r-- 1 admin staff 10095 7 5 15:13 shader1.love
drwxr-xr-x 7 admin staff 238 7 5 15:04 shader2
-rw-r--r-- 1 admin staff 2128 6 29 00:23 spider.lua
-rw-r--r-- 1 admin staff 884 6 18 16:31 test1.lua
-rw-r--r-- 1 admin staff 1568 6 18 16:31 test2.lua
-rw-r--r-- 1 admin staff 1769 6 18 16:32 test3.lua
-rw-r--r-- 1 admin staff 279 6 19 10:32 timeProfile.lua
Air:lua admin$ 

我們看到新生成了一個名為 shader1.love 的文件, 只要你的電腦上安裝了 Love2D, 就可以雙擊運行這個文件.

例程2

這是另一個用來演示 2D 場景下使用光照模型的例程, 原始下載鏈接 PixelCelShadingAmbientOcclusion2.love

對 .love 文件解包

整個操作過程跟第一個例程一樣,

Air:lua admin$ cd shader2
Air:shader2 admin$ love ../shader2
Error: main.lua:12: attempt to call field 'setCaption' (a nil value)
stack traceback:
main.lua:12: in function 'load'
[string "boot.lua"]:439: in function <[string "boot.lua"]:435>
[C]: in function 'xpcall'
Air:shader2 admin$ love ../shader2
Error: main.lua:75: attempt to call field 'drawq' (a nil value)
stack traceback:
main.lua:75: in function 'draw'
main.lua:59: in function 'draw'
[string "boot.lua"]:467: in function <[string "boot.lua"]:435>
[C]: in function 'xpcall'
Air:shader2 admin$ love ../shader2
Air:shader2 admin$ 

依次解決錯誤函數, 跟第一個例程的錯誤大同小異, 相同的地方就不多說了, 說兩個新錯誤: G.setCaption 改為 love.window.setTitle, G.drawq 改為 G.draw.

更新后代碼

修改后的 main.lua 文件代碼如下:


s = [=[
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.,:;'"?/\[](){}<>1234567890!@#$%^&*_-+=~`|
]=]

s = s.."\t"

local G, W, H
local sz = 2
function love.load ()
G = love.graphics
W = G.getWidth
H = G.getHeight
love.window.setTitle "Ambient occlusion test"
G.setBackgroundColor(35, 30, 65)

ao_toggle = 1 -- ao_toggle starts on
z = 30
time = 0

font_img = G.newImage "pixel_font.png"
font = G.newImageFont(font_img, s)
G.setFont(font)

G.setDefaultFilter("nearest", "nearest")
globe = G.newImage "globe.png"
img = G.newImage "pithos.png"
diffuse = G.newQuad(0, 0, 32, 43, 96, 43)

canvas = G.newCanvas(W()/sz, H()/sz)
canvas:setFilter("nearest", "nearest")

effect = G.newShader "shader.glsl"
effect:send("ao_toggle", ao_toggle)
end

function love.update (dt)
time = time + dt
z = z + math.cos(time)/3

local x, y = love.mouse.getPosition()
x = x/sz
y = y/sz
mouse = {x=x, y=y}
y = H()/sz - y
effect:send("light_pos", {x, y, z})
end

function love.mousepressed ()
ao_toggle = (ao_toggle + 1)%2
effect:send("ao_toggle", ao_toggle)
end

function love.draw ()

G.setCanvas(canvas)
G.clear(0,0,0,0)

draw()
G.setCanvas()
G.setShader()
G.draw(canvas, 0, 0, 0, sz, sz)
end

function printf (...)
G.print(string.format(...), 20, 10)
end

function draw ()
G.setShader()
G.setColor(220, 190, 0)
printf("Click toggles ambient occlusion.\nCurrently: %s.", ao_toggle == 1 and "on" or "off")
G.setShader(effect)
G.setColor(255, 255, 255)
G.draw(img, diffuse, W()/sz/2 - 34, H()/sz/2 - 18, 0, 1, 1, 16, 27)
G.draw(img, diffuse, W()/sz/2 + 34, H()/sz/2 + 18, 0, 1, 1, 16, 27)
G.setShader()
G.draw(globe, mouse.x, mouse.y-z, 0, 1, 1, 9, 9)
end

運行截圖如下:

打包

Air:shader2 admin$ zip -0 -r -X -q ../shader2.love ./*
Air:shader2 admin$ cd ..
Air:lua admin$ ls -al
total 184
drwxr-xr-x 19 admin staff 646 7 5 15:35 .
drwxr-xr-x 15 admin staff 510 7 4 09:55 ..
-rw-r--r--@ 1 admin staff 12292 7 5 14:41 .DS_Store
drwxr-xr-x 6 admin staff 204 7 1 21:11 2048
-rw-r--r--@ 1 admin staff 7691 7 5 10:05 Pixel1
-rw-r--r--@ 1 admin staff 7691 9 27 2012 PixelArtCelShading.love
-rw-r--r--@ 1 admin staff 10471 9 27 2012 PixelCelShadingAmbientOcclusion2.love
drwxr-xr-x 6 admin staff 204 7 4 00:01 c-test
drwxr-xr-x 5 admin staff 170 7 1 21:12 love
drwxr-xr-x 6 admin staff 204 7 1 21:06 particle
drwxr-xr-x 8 admin staff 272 7 5 15:21 shader1
-rw-r--r-- 1 admin staff 10095 7 5 15:13 shader1.love
drwxr-xr-x 7 admin staff 238 7 5 15:31 shader2
-rw-r--r-- 1 admin staff 13104 7 5 15:35 shader2.love
-rw-r--r-- 1 admin staff 2128 6 29 00:23 spider.lua
-rw-r--r-- 1 admin staff 884 6 18 16:31 test1.lua
-rw-r--r-- 1 admin staff 1568 6 18 16:31 test2.lua
-rw-r--r-- 1 admin staff 1769 6 18 16:32 test3.lua
-rw-r--r-- 1 admin staff 279 6 19 10:32 timeProfile.lua
Air:lua admin$ 

成功生成 shader2.love 文件, 雙擊運行正常.

參考

Pixel art with GLSL cel shade lighting concept


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM