对于水来说,应该具备下列的特征

  • 全反射,所有的物体都应该能被反射到,除了反射表面自身
  • 基于角度的反射
  • 深度穿透,近距离的水更加透明,远距离的水更加浑浊
  • 加入水纹或者水面法线

对于本文来说只实现前面2点

1.1视差
如果你之前渲染引擎使用单个pass,那么从现在开始,你需要至少2个passes(实际上至少是 N+1, N为可见的反射物体数量)

原因是,我们无法复用主场景的纹理用来反射。首先是因为视锥体可能非常大(比如,观察水表面从一个非常高的角度,我们只能在主场景中看到地面和水,还有被反射的大部分的天空)。 第二是因为视差,很不幸的是反射并不是主场景的完美复制,只是从不同的视觉位置的场景反射。下面的图表现了这个观点。

reflections

这意味着你需要渲染到纹理,我们将要渲染反射到纹理,然后使用这个纹理来渲染我们主场景的水面

因此,要得到反射纹理,我们首先需要通过反射相机来渲染我们的场景 如之前图中的 P` 位置。 首先我们需要找到反射相机的位置,或者更精确的说,相机矩阵的反射(因为我们还需要相机的角度), 我们能通过下面的算法得到反射矩阵

M’camera = Mreflection * Mcamera

Mreflection 是镜面反射矩阵,它能直接计算出基于平面一个点的反射

| 1-2Nx2 -2NxNy -2NxNz -2NxD |
Mreflection = | -2NxNy 1-2Ny2 -2NyNz -2NyD |
| -2NxNz -2NyNz 1-2Nz2 -2NzD |
| 0 0 0 1 |

(Nx,Ny,Nz,D) 是平面的方程 (xNx + yNy + zNz + D = 0) 的系数, 注意: (Nx, Ny, Nz) 也是这个平面的法线向量

Mcamera 是相机的矩阵变换, 在场景中也起着法线的功能, 要获取 ModelView 矩阵,你还需要将相机矩阵翻转

AS的计算函数如下
[cc lang=”actionscript3″]
//计算平面投影矩阵,n为平面法向量,p为平面上一点
public static function getReflectionMatrix(n:Vector3D,p:Vector3D):Vector.{
n.normalize();
var d:Number=-n.dotProduct(p);

var raw:Vector.=Vector.([-2*n.x*n.x+1,-2*n.y*n.x,-2*n.z*n.x,0,
-2*n.x*n.y,-2*n.y*n.y+1,-2*n.z*n.y,0,
-2*n.x*n.z,-2*n.y*n.z,-2*n.z*n.z+1,0,
-2*n.x*d,-2*n.y*d,-2*n.z*d,1]);
return raw;
}
[/cc]

1.2 镜面几何体
事实上,在之前的图形中,我们作弊了,我们旋转了镜面纹理180°使得它与原始图更相似,使得视差的效果能被看到。实际上,镜面图应该看起来像这样。

注意:球型物体的排序顺序在反射贴图中被翻转了,因为三角形顺序在主场景中是逆时针,但在反射中是顺时针

这对于你来说并不是问题,如果你所有的材质都是双面渲染的(比如,你不切除后表面) 或者如果你能设置以类似的方reflections-2式渲染管线。
这样你就能改变剔除方向。在我的例子中,我尽量设置双面都不剔除, 所以所有的东西都会出现在反射纹理中,反则一些几何体不会被渲染。

我们将要开发一个功能,使得相机总是(至少在大多数应用中)与视觉方向成直角并且处于中央,因此我们近需要翻转相机的Y轴方向然后改变渲染顺序就行(在翻转反射纹理后,看起来像 第一张图片中的 (3) 部分).
我们能再通过一个反射矩阵来实现

M”camera = Mreflection * Mcamera * Mflip

1.3 水下翻转
看看下面这张图

reflections-3

我们加入一个水下物体Q到我们的场景中,现在它应该不会出现在反射中,因为因为它不会阻挡射线 PB’B 和 PA’A. 但是我们并没有做射线追踪。 我们通过移动相机到镜像位置 P’ 然后像一般纹理一样渲染反射。 但是如你所见,对象Q阻挡了射线 P’A’A 因此物体Q会出现在我们的反射中

因此我们需要确定,任何物体在反射平面(水面)之下的都不能出现。我们能通过下面3种方式来实现。

1.在GPU中使用额外的裁切面。这可能非常快,也可能非常慢,取决与你的图形卡。
2.在反射渲染中使用斜投影矩阵,你可以在 http://www.terathon.com/code/oblique.html 了解它的相关知识。这是一个非常cool的技术,但是它在远平面中的相机中表现的并不好。
3.手动修剪 Pixel Shaders. 这会浪费一些GPU的循环,但是却非常简单。

我选择使用方法3,因为斜投影矩阵并没有看起来那么好,当相机的角度很大(远平面都变成一个非常奇特的效果)。 剪裁功能非常容易加入到下面的代码中,在所有的 Pixel Shaders 开始之前(更确切的说是用在所有的反射物体上)

[cc lang=”actionscript3″]
//float clipPos = dot (interpolatedVertexEye, clip_plane.xyz) + clip_plane.w;
dp3 result.x eyePos.xyz plane.xyz
add result.x result.x plane.www
//if (clipPos < 0.0) {
// discard;
//}
slt ifReg.x result.x fc0.x //fc0.x == 0
[/cc]

你必须计算顶点Shader(将顶点坐标转换到视野坐标下 VertexEye = Mmodelview * Vertex)中计算平面到视角的差值. 如果你不要裁剪,只要设置 clip_plane 法线(xyz) 为0. 这样所有的像素都会被渲染

1.4 把一切都合并起来
在开始主渲染pass之前(提前或者推迟)做下列这些事

1.创建一个需要被反射的渲染列表(参数为它们的反射面)之后 for each 反射面:
2.计算反射相机矩阵 M”camera = Mreflection * Mcamera * Mflip
3.设定相机矩阵(你可以通过使用翻转投影矩阵来优化,但是在这里不适用)
4.设置裁剪面到反射平面
5.渲染整个场景
6.保存反射纹理应用到反射物体上

如果你使用HDR, 你就不要用使用色调投影到反射纹理上,除非你想做一些非常奇特的效果。

2.渲染反射物体
这一步非常的简单,确保你掌握了所有必须的参数。你还需要决定哪一个 render stage 来做这些事。我使用Transparent.
这样水面就是基于一个场景的透明表面。但是你可以增加一些其他的pass在之前或者之后。

你需要掌握这些
反射相机矩阵 M”Camera
投影矩阵你需要渲染反射 Mprojectionreflection(通常是和你的主相机是使用的同一个矩阵)
反射纹理

2.1 顶点Shader
参数
vertex3D vertex;
Matrix3D o2v_projection;
vertex3D interpolatedVertexObject;

m44 vt0 02v_projection vertex3D(vertex.x,vertex.y,0.1)
interpolatedVertexObject = vt0

我们在这里增加了一个约束,水面在物体本地坐标系中是属于 XY 平面。 它不是确实不是必要的如果你有适当的反射平面,但是我发现这样做起来更容易。仅仅使用XY平面作为反射平面然后适当摆放你的物体(水体)

事实上,有另一个很酷的诀窍,我们可以使用水体的底部作为我们的水体。它将会在Shader中被扁平化,我们可以使用 Z data 来检测水中点的深度。 这部分以后再说。

o2v_projection 仅仅是 Projection * modelView 矩阵的名称。 我喜欢这样给矩阵命名,这样跟助于记忆, 使用它们所描述的坐标系统。 比如 Object To View * Projection

interolatedVertexObject 仅仅是顶点坐标在本地空间的坐标系统,我们将需要在反射纹理中看到它。

2.2 Fragment Shader
参数
matrix3D o2v_projection_reflection
sampler2D refletion_sampler
vertex3D interpolatedVertexObject

vClipReflection = o2v_projection_refletion * vectex(interpolatedVertexObject.xy,0.0,1.0);
//设备反射
vDeviceReflection = vClipReflection.st / vClipReflection.q
//纹理反射
vTextureReflection = vec2(0.5,0.5) + 0.5 * vDeveiceReflection;
//反射纹理颜色
reflectionTextureColor = texture2D(reflection_sampler, vTextureReflection);

//反射纹理的Alpha 可能 > 1
reflectionTextureColor = 1.0;

mov oc reflectionTextureColor

o2vProjection_reflection 是 Projection * ModelView 矩阵, 用来在反射期间进行渲染

Mprojectionreflection * (M”camera)-1 * Mobject

如名字表现的,这是将物体空间坐标系统转换到切面坐标系统的反射相机
在fragment shader 中,我们仅仅重复了整个完整的变换管线在反射渲染中。 渲染然后使用2D坐标系采样。要做到这样,我们首先需要初始化和为转换的顶点物体坐标,因此他们被差值通过vertex shader(interpolatedVertexObject)

设置反射alpha 为 1 因为我使用了 HDR 缓冲,因此它的的 alpha 最终会变成一个非常奇怪的值

Continue reading

挖坑,慢慢填的事情, 我最喜欢做了 😀

滑动鼠标查看效果

之前一直想做这个效果的,但是一直没空研究的说。 最近 SParticle 更新了,增加了 Heat 和 Phantom. 后者效果不说了,残影的一贯做法。 正好这套工具 + Away3D 都是开源的,想看看做法,结果 Heat 貌似是 Sparticle 独有, Away3D 里看不到这个效果,索性自己实现, 发现这个并不难。

一共5个步骤
1.对背景进行一次 RenderToTexture, 如果背景有动态物体,则需要每帧 RTT
2.绘制刀光,水波,粒子等几何体顶点,随意
3.使用 RTT纹理 对几何体进行贴图,可以和原贴图 mul 或者 add
4.根据几何体坐标,算出几何体在在 RTT 纹理中的全局坐标,并计算在 RTT 纹理中的全局 uv
5.根据全局uv 对 RTT纹理 进行采样

最近无聊把一堆东西移植到这个2D框架上, 这次是条带效果~~ 条带代码来自 ?@朝朝姐夫 做了些修改

和之前模型一样,还是已扩展类的形式存在,并且可以参与Starling的深度排序,坐标定位,缩放等

效果预览,移动鼠标即可

首先创建一个名为 RibbonTest 类 继承自 Starling 的 DisplayObject

[cc lang=”actionscript3″]

public class RibbonTest extends DisplayObject

public function RibbonTest(speed:Number = 0.1, detailLevel:uint = 10, width:uint = 1)
{
super();

this.speed = speed;
this.touchable = false;
ribbonWidth = width;

controlPoints = new Vector.();
for(var i:Number = 0;i < detailLevel; i++) { controlPoints.push(new Vector3D(0, 0, 0)); } vertexList = new Vector.
(detailLevel*2,true);
rawPositionsBuffer=new Vector.(detailLevel*6,true);
rawUvBuffer=new Vector.
(detailLevel*4,true);
rawIndexBuffer=new Vector.((detailLevel-1)*6,true);

createRibbon();
createProgram();
createBuffer();
}

[/cc]

构造函数中 speed 为缓动的速度,后面会用到经典的缓动公式 (end-start)/speed;

detailLevel 控制点数量,控制点越多条带在转交的效果越细致

Continue reading

好久没有写教程神马的了,最近比较忙的说。

回到正题,最近打算在游戏中创建一个3D模型,由于游戏是Starling 2D游戏,因此打算把模型丢到UI里,3D模型出现在2D游戏里的话一定是高端大气上档次!

之前老外写的教程都是 Starling 和 Away3D 杂交, 但是仅仅在UI上摆一个3D模型就整合一个Away3D 实在是大材小用,也可以说是浪费性能, 索性自己来写一个。

先上个图。

一切以兼容为主, 实现一个类 DisplayObject3D 继承自 Starling 的显示对象 DisplayObject

之后override 其的 render 方法

[cc lang=”actionscript3″]

public override function render(support:RenderSupport, parentAlpha:Number):void

[/cc]

do you see that? ?这里我们能获取到一个 ?RenderSupport.

这可是一个好东西, 我们能从中获取到 mvpMatrix 即 模型视图投影矩阵, 有3d 和 2d 2个版本。

然后,我们可以从 Starling.current.context 获取到 GPU的 API模组, 有了这些东西,想干啥都行了。

Continue reading

今晚恒大赢了啊,为了看比赛不困喝了一杯咖啡,导致现在还睡不着的说~~~ ?比赛最后2分钟紧张死我了,守门员曾诚还被裁判警告拖延时间的说~ 哈哈。

之前的之前,今天才买2个月的三星显示器全屏发红的影响我Dota的发挥,心想900块的显示器果然不靠谱什么的,正在填下京东退货单的时候灵机一动,在红屏的时候开启我的PS3,切换信号源后一切正常了!!! 果然是连接我笔电的HDMI信号线的问题!!! 话说这线是买PS3时候某同事挑的,入手后这线只要扭曲到90度就无信号了~ (我这人又很节省嘛~)于是就将就用了,平时也相安无事,最近2个月果然连抗干扰这功能也费了吗?都搞的我显示器发红了(也许是被我笔电的排气口长期烧烤的缘故吧 :D). 一怒之下吃完饭,赶在恒大开始前飚车去买新线回来了~一切又完好如初!!

之前的之前的之前,中午把使命召唤10通关了的说~ 虽然配置碉堡了(我的小霸王关闭了 抗锯齿,景深, 全屏幕真实光反射 后高分辨满帧坚挺的说) 但是画面渣透了!!! 不过剧情真的很爽就是 了,秒9代几百条街!! IW 社果然不是盖的说。。。

因为某些原因,最近又开始撸Starling了~ ?命运总是让我无法彻底割舍你呢~~ 于是乎,下周吧,发一个在Starling下创建3D模型的教程吧~~ 支持Starling原生的画家算法的说。。。。

最后来个配图好了~~ 就决定你是了 – 使命召唤 – Ghosts

无意中发现《境界的彼方》完整PV出了,贴一下。。。

话说最近大作又扎堆出了~~下周三 使命召唤, 这周蝙蝠侠, 命运之镜HD~

突然又想败家一个Gundam 模型~~

沉默了一段时间,现在开始回归,有好多好东西准备分享一下

总之,运行后就是这样
p4

1.创建Robot类
2.添加 Hero 和 Robot 受伤 和 死亡 动作
3.添加 Robot 的AI 使之会跟踪玩家并攻击
4.添加音效

在 ActionSprite.lua 中添加方法
[cc lang=”lua”]
function ActionSprite:createBoundingBoxWithOrigin(origin,size)
local boundingBox = {}
boundingBox.original = CCRect()
boundingBox.actual = CCRect()

boundingBox.original.origin = origin
boundingBox.original.size = size
boundingBox.actual.origin = ccpAdd(ccp(self:getPositionX(),self:getPositionY()),ccp(boundingBox.original.origin.x,boundingBox.original.origin.y))
boundingBox.actual.size = size

return boundingBox
end
[/cc]
这里创建一个包围盒,其中 original 为 角色原始矩形
actual 为真实矩形,它会根据角色的坐标,不断修改该矩形的 x,y, 这个函数只在初始化时调用
Continue reading

之前已经实现了在场景上添加英雄,并使英雄播放待机动作,接下来让英雄在场景动起来

本游戏的代码已开源,包含游戏资源
git地址
https://github.com/dreamfairy/PrompaLua

首先我们创建用户控制层 HudLayer.lua 包位置为 scenes.layers.HudLayer.lua

HudLayer.lua 的内容为
[cc lang=”lua”]
local HudLayer = class(“HudLayer”, function()
return display.newLayer()
end)

local DPad
function HudLayer:ctor()

end

function HudLayer:getDPad()
return DPad
end

return HudLayer
[/cc]

可以看到我们实现了一个 getDPad()方法,但是DPad目前还是nil, 接下来开始创建 DPad 用户操控面板
创建 SimpleDPad.lua 包位置为 scenes.Controller.SimpleDPad.lua

Continue reading

=。= 很久没更新博客,偷懒了好久。 受某损友的影响接触到 quick-cocos2dx. 一个使用lua来编写游戏的框架,我使用的内核版本是 cocos2dx 2.1.5。 嘛~ DreamFairy 又是初次接触Lua, 于是决定边查Lua API手册边写一个横版过关游戏。

游戏的原型是 Allen Tan 的文章 http://www.raywenderlich.com/24155/how-to-make-a-side-scrolling-beat-em-up-game-like-scott-pilgrim-with-cocos2d-part-1原游戏是使用 objective-c 编写的IOS游戏教程

国内也有同学写了一篇C++ 的教程
http://blog.csdn.net/akof1314/article/details/8549150

so~ 我来搞一个lua版本的教程

本游戏的代码已开源,包含游戏资源
git地址
https://github.com/dreamfairy/PrompaLua

1.进入 quick-cocos2dxbin 目录,使用CMD打开create_project.bat 输入 cn.dreamfairy.prompalua 该包名创建项目
2.删除Demo项目文件MainScene.lua
3.修改 Config.lua 添加一些游戏的资源信息
Config.lua 内容:
[cc lang=”lua”]

— 0 – disable debug info, 1 – less debug info, 2 – verbose debug info
DEBUG = 2
DEBUG_FPS = true

— design resolution
CONFIG_SCREEN_WIDTH = 640
CONFIG_SCREEN_HEIGHT = 960

–资源
CONFIG_ROLE_SHEET_IMAGE = “pd_sprites.pvr.ccz”
CONFIG_ROLE_SHEET_FILE = “pd_sprites.plist”

–地图
CONFIG_TILEMAP_FILE = “pd_tilemap.tmx”

— auto scale mode
CONFIG_SCREEN_AUTOSCALE = “FIXED_WIDTH”

[/cc]
Continue reading