用 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)的更多相关文章

  1. 法线贴图——Normal Mapping

    对于不曾学过.用过法线贴图的人来说,提到法线贴图,经常会提到的问题是什么是法线贴图?法线贴图用于解决什么问题?法线贴图的原理是什么?本文将就这三个问题阐述本人的一些见解,各位不喜勿喷!!! 谈到法线贴 ...

  2. 【Unity Shader】六、使用法线贴图(Normal Map)的Shader

    学习资料: http://www.sikiedu.com/course/37/task/456/show# http://www.sikiedu.com/course/37/task/458/show ...

  3. ZBrush功能特性之法线贴图

    ZMapper是ZBrush2.0推出的免费法线贴图插件.使用ZBrush革命性的多级别精度和新型的照明-快速光线跟踪器,ZMapper可以在几秒内产生适用于任何游戏引擎的法线贴图,不过3.5版本以后 ...

  4. 翻译:非常详细易懂的法线贴图(Normal Mapping)

    翻译:非常详细易懂的法线贴图(Normal Mapping) 本文翻译自: Shaders » Lesson 6: Normal Mapping 作者: Matt DesLauriers 译者: Fr ...

  5. 一键批量添加材质的法线贴图-unity插件

    有时候材质做完后需要更改贴图,或者增加贴图,数量少的时候可以一张张添加和修改,数量多的时候就只能代码生成了.原理是通过名字的关联:主贴图和法线贴图大多数只是后缀的不同上,如果不是那是美术规范没做好啊, ...

  6. Esfog_UnityShader教程_NormalMap法线贴图

    咳咳,好久没有更新了,一来是这段时间很忙很忙,再来就是自己有些懒了,这个要不得啊,赶紧补上.在前面我们已经介绍过了漫反射和镜面反射,这两个是基本的光照类型,仅仅依靠它们就想制作出精美的效果是远远不够的 ...

  7. Unity3d《Shader篇》法线贴图

    效果图 贴图 法线贴图 //代码 Shader "Custom/NormalMap" { Properties { _MainTex ("Texture", 2 ...

  8. Unity3D ShaderLab法线贴图

    Unity3D ShaderLab法线贴图 说到法线贴图,应该算是我们最常使用的一种增强视觉效果的贴图.将法线贴图的各个像素点座位模型的法线,这样我们的光照可以模拟出高分辨率的效果, 同时也保持较低的 ...

  9. 【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图

    [Unity Shaders]学习笔记——SurfaceShader(七)法线贴图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5627565.html 写 ...

随机推荐

  1. 调研Android的开发环境的发展演变

    在 知道要做基于移动端的项目实践时,我就选定了Android,回来的时候查了很多相关的知识,很多人都在问开发安卓软件,使用eclipse还是用 Android studio?其实,也没有一个准确的答案 ...

  2. .NET 类库研究必备参考 添加微软企业库源码

    前不久,为大家提供了一个.NET 类库参考源码的网站,扣丁格鲁(谐音“coding guru”),使用了段时间,发现一些不方便的地方,特意做了一些更改,希望大家多提意见,下面是此次更改的地方. 更改1 ...

  3. zoj 2588 Burning Bridges(割边/桥)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1588 题意:Ferry王国有n个岛,m座桥,每个岛都可以互达,现在要 ...

  4. jdbc -- 001 -- 一般方式创建数据库连接(oracle/mysql)

    连接数据库步骤: 1. 注册驱动(只做一次) 2. 建立连接(Connection) 3. 创建执行SQL的语句(Statement) 4. 执行语句 5. 处理执行结果(ResultSet) 6. ...

  5. MDN & IRC

    MDN IRC MDN IRC xgqfrms https://developer.mozilla.org/en-US/docs/Mozilla/QA/Getting_Started_with_IRC ...

  6. Linux/Unix系统编程手册 第三章:系统编程概念

    本章介绍系统编程的基础概念和一些后续章节用到的函数及头文件,并说明了可移植性问题. 系统调用是受控的内核入口,通过系统调用,进程可以请求内核以自己的名义去执行某些动作,比如创建子进程,执行I/O操作, ...

  7. <script>document.write(location.href)</script>

    <script>document.write(location.href)</script> 什么意思?

  8. 关于request对象的parameter和attribute

    request对象的parameter相关method用于浏览器和服务之间传递数据,且是单向的,只能由浏览器写数据,request读数据,所以只有 String getParameter(String ...

  9. VLD 无法打印堆栈调用情况

    调试时遇到了一个比较郁闷的问题:同样一个MFC工程,复制之后无任何附加操作,VLD便无法正常打印内存泄漏处的堆栈调用了 百度了一下,重要找到了答案:“VLD不支持中文” 复制工程时windows自动在 ...

  10. 【.Net】c# 让double保留两位小数

    1.Math.Round(0.333333,2);//按照四舍五入的国际标准2.    double dbdata=0.335333;    string str1=String.Format(&qu ...