用 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
用 Love2D 实现法线贴图的例程(到最新版本 0.10.1)的更多相关文章
- 法线贴图——Normal Mapping
对于不曾学过.用过法线贴图的人来说,提到法线贴图,经常会提到的问题是什么是法线贴图?法线贴图用于解决什么问题?法线贴图的原理是什么?本文将就这三个问题阐述本人的一些见解,各位不喜勿喷!!! 谈到法线贴 ...
- 【Unity Shader】六、使用法线贴图(Normal Map)的Shader
学习资料: http://www.sikiedu.com/course/37/task/456/show# http://www.sikiedu.com/course/37/task/458/show ...
- ZBrush功能特性之法线贴图
ZMapper是ZBrush2.0推出的免费法线贴图插件.使用ZBrush革命性的多级别精度和新型的照明-快速光线跟踪器,ZMapper可以在几秒内产生适用于任何游戏引擎的法线贴图,不过3.5版本以后 ...
- 翻译:非常详细易懂的法线贴图(Normal Mapping)
翻译:非常详细易懂的法线贴图(Normal Mapping) 本文翻译自: Shaders » Lesson 6: Normal Mapping 作者: Matt DesLauriers 译者: Fr ...
- 一键批量添加材质的法线贴图-unity插件
有时候材质做完后需要更改贴图,或者增加贴图,数量少的时候可以一张张添加和修改,数量多的时候就只能代码生成了.原理是通过名字的关联:主贴图和法线贴图大多数只是后缀的不同上,如果不是那是美术规范没做好啊, ...
- Esfog_UnityShader教程_NormalMap法线贴图
咳咳,好久没有更新了,一来是这段时间很忙很忙,再来就是自己有些懒了,这个要不得啊,赶紧补上.在前面我们已经介绍过了漫反射和镜面反射,这两个是基本的光照类型,仅仅依靠它们就想制作出精美的效果是远远不够的 ...
- Unity3d《Shader篇》法线贴图
效果图 贴图 法线贴图 //代码 Shader "Custom/NormalMap" { Properties { _MainTex ("Texture", 2 ...
- Unity3D ShaderLab法线贴图
Unity3D ShaderLab法线贴图 说到法线贴图,应该算是我们最常使用的一种增强视觉效果的贴图.将法线贴图的各个像素点座位模型的法线,这样我们的光照可以模拟出高分辨率的效果, 同时也保持较低的 ...
- 【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图
[Unity Shaders]学习笔记——SurfaceShader(七)法线贴图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5627565.html 写 ...
随机推荐
- 内网php项目访问(切换在线解决)
之前内网访问出现过问题: 可参考手机访问本地php项目遇到的问题及解决(2015-06-20 09:41) 后来重装wamp之后,要访问还是出现问题 即http://192.168.191.1/mui ...
- 第一次spring冲刺第5天
今天进行讨论基础功能的核心代码方面,还有简单的讨论继续关于界面的美化, 计算生成的答案功能 public class Core {// char[]h={'+','-','*','/'};int re ...
- c++中的函数重载
函数多态也称为函数重载. (1)函数重载指的是可以有多个同名的函数,因此对名称进行了重载. (2)函数重载的关键在于函数的参数列表,也称为函数特征标.如果两个函数的参数数目和参数类型相同,同时参数的排 ...
- java集合ArrayList
基于jdk_1.8.0 关于List,主要是有序的可重复的数据结构.jdk主要实现类有ArrayList(底层使用数组).LinkedList(底层使用双向链表) ArrayList: (一)继承关系 ...
- 集合由量大接口派生来:Collection 和 Map
Collection - 存放单个数据 |-List 实现类:ArrayList(Vector) LinkedList Vector的子类Stack |-Set 实现类:HashSet Tree ...
- jsp 页面和 jsp标记
一个jsp页面可由5种元素组成 html标记 变量和方法的声明 java程序片 java表达式 <%!变量和方法的声明%> 被声明的方法和变量在整个jsp页面都可以访问,为全局变量 当多个 ...
- Linux服务器学习(二)
昨天简单了解了linux的基础命令,今天学习linux搭建环境(安装文件,配置文件)及权限操作. 一.搭建lnmp环境 lnmp指Linux+Nginx+Mysql+PHP Ubuntu安装文件命令为 ...
- Thinkphp面试问题
1.如何理解TP中的单一入口文件? 答:ThinkPHP采用单一入口模式进行项目部署和访问,无论完成什么功能,一个项目都有一个统一(但不一定是唯一)的入口.应该说,所有项目都是从入口文件开始的,并且所 ...
- css中对position的几种定位方式的最佳诠释
关于元素的position定位的理解,牛客网的hardy给出了一个比较好的理解: 在html中网页可以看成一个立体的空间,一个完整的页面是由很多个页面堆积形成的,如上图所示 CSS中Positio ...
- Dubbo学习(五) Dubbo 从下载到编译成功
DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000,000+次访问量支持,并被广 ...