用 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

posted on 2016-07-20 11:43  自由布鲁斯  阅读(2403)  评论(4编辑  收藏  举报