引擎设计跟踪(九.8) Gizmo helper实现与多国语言
最近把gizmo helper的绘制做好了.
1.为了复用代码,写了utility来创建sphere, cube, cylinder, plane, ring(line), circle(solid) 这些基本物体, 顺便把天空球的创建代码改用utility函数,以后DS的灯光球和椎体等等也可以复用了.
2.本来很简单的以为使用正交投影(orthographic projection)就可以画出大小不变的gizmo.但是却忽略了一个问题: 因为helper的世界变换矩阵跟选中的物体的一样,所以理论上gizmo的轴朝向是跟物体的轴(透视投影后的)朝向一致,位置也和物体(透视)投影后的坐标一致. 但是用正交投影以后,发现旋转和位置都不匹配, 仅仅是大小不变. 比如下图,同一个点, 投影模式不一样,那么最终的位置也不一样:
所以正确的做法应该是用透视投影,并保持投影后的大小不变.思路就是根据投影后的大小反推原始参数,比如这里的求助贴: http://gamedev.stackexchange.com/questions/24968/constant-size-geometries 作者自问自答,使用的是投影大小反推缩放值. 我用的方法类似,也比较简单容易理解:使helper和摄像机保持固定距离.
其实这个固定距离跟投影后的固定大小一样, 是一个magic值,只不过投影后的大小这个值更直观. 而且固定距离也可以先用投影后的固定大小算出来, 但是需要相机的投影参数. 我这里没有相机投影参数,所以做的很简单:
//////////////////////////////////////////////////////////////////////////
void AxisGizmo::updateRelativeCamera(const Vector3& cameraPos,const Quaternion& cameraRotation)
{
//update gizmo transform //note: keep a constant distance form camera so that gizmo size looks unchanged
static const scalar DISTANCE = ;
const Vector3& pos = mTarget->getPosition();
Vector3 movedPos = cameraPos + (pos - cameraPos).getNormalizedVector()*DISTANCE; const Vector3& scale = Vector3::UNIT_ALL;
const Quaternion& rotate = mTarget->getRotation();
mState->setWorldTransform(movedPos, scale, rotate, cameraRotation);
}
3.上一步完成以后, Roation helper也有一个问题, 比如max或者maya的helper,最外层的那两个圆环一直朝向摄像机. 这个做法也有很多种,比如3D billboard,或者直接在投影后的位置画2D ring.
我用的是billboard的方法.首先, ring创建在view space (对象在object space的顶点坐标直接设置为在view space的坐标) 同时world transofrm为view.inverse(), 这样根据world*view=identity, 相当于顶点的原始坐标和直接在view space一致. 同时注意到view.inverse() (matrix 3x3) 正好就是camera的rotation matrix,所以billboard the world matrix计算如下:
world = gen_transform(pos, scale, matCameraRotation) 或者 world = gen_transform(pos, scale, quatCameraRotation)
virtual void setWorldTransform(const Vector3& pos, const Vector3& scale, const Quaternion& rotation, const Quaternion& camearRotation)
{
AxisGizmoState::setWorldTransform(pos, scale, rotation, camearRotation);
//TODO: update view space ring transform ////note: the rings are created to orient the Vector3::UNIT_Z by default (vertices are in view space already)
////now just apply the rotation = inverse(viewMatrix33) = camera rotation, to make world*view == identity
Matrix44::generateTransform(mViewSpaceRingTransform, pos, scale, camearRotation);
}
其实做了这么多,就是为了在常规管线(world*view*projection)里去掉旋转,得到(identity + worldpos+viewTransPos)*projection即projected pos.于是在shader框架里又加了一组自定义的semantics(shader自动变量)为BillboardView/BillboardViewProjection/BillboardWorldView/BillboardWorldViewProjection, 直接跳过world*view的旋转,只使用位移, 这样可以方便在shader里面直接使用billbard.
感觉这样的billboard更高效,不需要反算world rotation,再乘上去:inverse(view)*view*projection, 而直接是identity.setpos(worldPos+viewTransformTranslate)*projection 去掉了2步无用的操作.而且用起来更方便了.因为这几个变量就像WORLD_MATRIX, WORLD_VIEW_PROJECTION一样,被封装到shader框架的自动变量里,shader框架会去自动计算和更新(如果用到的话),不用再像上面那样,起码要有一个billboard基类来复用CPU端代码,或者根据情况,逐个情况手动在CPU端计算.
唯一需要注意的是billboard物体的local space的几何顶点必须相当于直接在view space创建, 面向相机,一般为Z+或者Z-,比如我的右手系,view空间的视向量为Z-,那么billboard物体的面片在local space的朝向应该为Z+.因为目前没有billbard相关的系统所以没有测试用例,而这个里的gizmo helper用统一的shader也懒得再分出来一个shader专门测试shader变量,所以目前还是手动CPU代码计算变换.
4.注意max/maya中的rotation helper, 3个轴对应的圆圈, 只显示了前面部分,后面不显示.这个有点像back face culling, 但是绘制的是line而不是triangle.
想到了3个方法去做这个效果. 第一是用球体+alpha贴图, 第二是用很窄很细的cynlinder来代替圆圈线, 第三种是shader里面处理. 前两种都是画的面片,所以开启back face culling就可以了.试了第二种,效果还可以,就是有时候线过粗,有点变形.
第三种方法是在shader里面clip掉背对相机的点.如何判断一个点是朝向相机还是背对相机? 注意一个点的view space的normal, 正面的点,他们的view space normal都朝向观察点(view space的原点).如下图的viewspace 顶视图:
所以只要clip掉normal.z > 0 或者<0 的像素(根据左右手系)就可以了.注意到sphere/circle通常在local space的原点都是(0,0,0) 那么球面/圈上的点的local space法向量就是normalize(localpos).有了以上分析,就可以写shader了:
void BladeVSMain(
float4 pos : POSITION,
uniform float4x4 wvp_matrix,
uniform float4x4 wv_matrix,
out float4 outPos : POSITION,
out float3 outViewNormal : TEXCOORD //view space normal
)
{
outPos = mul(pos,wvp_matrix);
float3 normal = pos.xyz;
outViewNormal = normalize( mul(normal, (float3x3)wv_matrix) );
} float4 BladeFSMain(
in float4 pos : POSITION,
in float3 viewNormal : TEXCOORD
) :COLOR0
{
clip( viewNormal.z );
//object_diffuse_color is a built-in semantic for per-instance diffuse
return object_diffuse_color;
}
到这里遇到另外一个问题,是shader变量被优化的问题.本来自动变量WORLD_VIEW_WMATRIX是float4x4, 我在绑定shader参数的时候会做大小和类型检查,但是发现类型不匹配的assertion failure, 一看原来shader里面虽然定义了float4x4,但只用到了world_view的3x3部分(旋转变换), D3DCompile把这个变量的类型也优化成float3x4, 导致跟定义的不匹配, 于是将精确匹配检查改为兼容性检查,只要可兼容,就可以绑定, 这样问题也解决了.
5.helper 的hit test - 根据屏幕坐标得到空间射线(这个很早做的,不多说了),然后根据射线检测出被选中的helper的对应的轴.
问题是箭头尾部的线, 怎么做相交查询. 因为一条线很难被选取,所以要扩大一定范围.本来想直接在投影后的空间做2D相交检测,这样的话,扩大的范围也好计算,但是觉得太麻烦, 最后用的是3D空间的box, 这样box的切面大小就是扩大的范围了. 注意,由于这个box只用于相交检测,不用于渲染,所以不需要创建显卡资源.
rotation helper的轴是三个线圈, 也不好做相交检测, 幸好有一个球在, 根据球面交点的局部坐标,(就知道在哪个轴了)比如交点的localpos.x == 0 说明红色圈(绕x轴的圈)被选中.而这样扩大范围也不难,加上误差比较就可以了.
最后说一下多国语言,这个很早就在考虑,但是没时间做, 但是想了想这个属于很基本的特性, 越到后面越难加, 工作量越大, 索性现在做了.
多国语言原理简单说就是程序中用ID而不用硬编码字符串, 运行时加载语言文件表, 查表得到翻译的字符串.这里面有几个问题需要考虑, 第一,程序员在写代码时,必须有原始语言文件,不断添加资源, 这样很麻烦. 第二,策划在写剧情脚本时, 也要很繁琐的添加文字,然后得到ID, 一样麻烦. 第三,语言文件越来越大可能有无效的信息,需要删除无用的数据. 整个处理流程考虑清楚了,就可以动手了.
所以最初的设想是这样:对于程序员来说, 程序用宏开关, 本地编译时, 使用硬编码的字符串. 编写一个lang-build-tool 来处理程序中出现的字符串,生成内置语言表. 最终发布时, 根据语言查表, 这个语言表不是ID-String map, 而是String List, 就是内置语言(硬编码语言)的列表. 这个表是用来做批量翻译用的,已经与程序源代码编写无关.
根据内置表人工翻译出来的的语言表, 根据在最终发布时一起加载, 组成内置表到目标语言的String-String map.程序在实时根据内置字符串查表就可以得到目标语言.这么做程序员就不用理繁琐的文字资源添加和修改了. 能够这样做前提是程序中自身用到的字符串不多, 而且大都十分短小(每个单元是1-3个单词). 这样效率没有什么大问题.
对于策划来说, 可能字符串的量很大, 而且字符串很长, String-String map的效率可能会低,而且程序已经不需要原始String. 所以做法稍微不同, 任何配置工具都会在内部自动调用语言表的接口, 添加文字到原始语言表, 这个跟程序源代码产生的表类似, 只用于最后批量翻译, 而且与程序生成的内置表不同, 不需要再加载. 翻译后的表是线性String List, (不需要与原始表构成String-String map), 直接用index索引, 而index是多少程序员不需要关心, 因为这个index也存在策划配置文件里面自动生成.
比如程序中的宏如下:
#define MULTILANG 0 #ifndef MULTILANG
# define XLang(_str) TEXT(_str)
#else
# define XLang(_str) ILangTableManager::getSingleton().getBuiltInLangString( TEXT(_str) )
#endif
这样程序员只要知道,对于不需要被翻译的字符串,使用TEXT("Something"), 而对于需要被翻译的字符串,使用XLang("Translation"), 内部调试的时候宏开关关闭, 程序员只需要专注于代码就可以了.
同时lang-build-tool 会处理(read-only-parsing) 所有的源代码, 找到XLang("")内部的字符串并添加到内置语言表里, 这个内置语言表用批量作翻译, 和运行时加载.
最终发布时, 将改宏开启, 语言模块会加载 源代码生成的内置表, 和翻译后的目标语言表, 组成map, 供运行时查询, 策划的语言表也会被加载, 被策划配置数据直接使用.
同时, 需要提供一个翻译工具.因为语言表可能是二进制的.即便不是二进制,是纯文本, 也要有对比功能.翻译工具加载原始语言和目标语言的表,对比不同, 提供UI界面给翻译人员做批量翻译.暂时把这个工具叫做lang-diff-edit-tool(这个工具还没有写...) 最终翻译时, 翻译人员需要翻译两张表, 一个是程序源代码被lang-build-tool处理后生成的表, 另一个是各种策划工具生成的一张语言表. 这样批量翻译也没有问题了.
以上只是简单流程, 没有考虑语音和文字图片的因素.
声音资源话,可能要分成普通声音资源(不需要翻译)和语音资源, 文字图片也需要单独分出来, 以便以后批量翻译处理. 最大的问题在于语音和字幕,字幕是需要结合情景来翻译的,而不能简单机械的处理.而且最头痛的是模型动画(面部)口部的动作也要做单独"翻译"处理. 笔者玩过<星际争霸2>的英文版和中文版, 可以看出中文版的角色口型跟英文版的不同, 是配合中文发音的. 不知道这个是美术为每句话专门调的动画呢(-_-!), 还是有工具自动处理的.
理想的情况是有对话语言编辑工具, 将各种基本发音(元音/辅音) 自动作出模型表情动画( 记得OGRE有一个面部表情的例子, 就是根据发音动态做出口型的), 同时需要有语音引擎,将文本转成数字(音标)信息, 用这个信息来处理面部表情.而且,由于一个声音可长可短, 可能仍然不能机械翻译-做动作, 要结合配音演员的声音数据, 分析每个音节的长短, 来做自动动画的时间控制和匹配... 最后,可以加上表情选项, 每句话都可以有不同的表情,这样一个场景对话工具和语言翻译工具才算完美.
上面只是大话一下, 回归现实, 目前我只做了简单的文字翻译... 由于现在没有翻译界面的工具,而我用的是二进制语言表, 所以目前是宏来定义了一堆字符串, 这些信息放在一个或者几个header被lang-build-tool处理, 为了测试语言模块, 写了中文对应的header(这些中文header编译代码时用不到),生成了中文的语言表.现在已经可以动态切换语言了. 这么做对于程序员来说其实也可以, 唯一的缺点就是需要往header里面加字符串和rebuild, 优点翻译是更显性化, 更可控, 也许以后就这么做了.
另外,语言表的字符串,最好用utf8, 这样不用考虑wchar_t的大小(UTF16/UTF32) 或者endian的问题了, 即便是二进制文件, 也用utf8保存单个字符串, 加载时转换为多字节或者wstring.
还有,对于类工厂,如果使用字符串做类创建,那么这个字符串最好不要被翻译, 因为这个类型字符串可能被保存在资源文件里面, 如果切换语言, 一个类被注册成了其他语言字符串, 使用原来资源中的字符串将找不到注册信息. 当然也可以在加载资源的时候,手动将类型信息也翻译了, 不过那么做没太大必要.还有,有部分字符串是在UI模块里面自动翻译的(手动调用接口翻译),特别是属性表的文字. 主要原因是翻译起来不方便, 或者是需要写入资源的属性信息,加载资源时还需要原字符串.
最后贴一张图纪念一下最近的工作, 以后更新仍然会很慢,因为工作很忙, 业余只有周末有点时间.后面会把translate/rotate/scale工具做完,因为现在只是显示了helper,还没有做功能.然后开始搞动画,拖了好久了...
图里面有几个bug待会儿修一下..
引擎设计跟踪(九.8) Gizmo helper实现与多国语言的更多相关文章
- 引擎设计跟踪(九.14.2a) 导出插件问题修复和 Tangent Space 裂缝修复
由于工作很忙, 近半年的业余时间没空搞了, 不过工作马上忙完了, 趁十一有时间修了一些小问题. 这次更新跟骨骼动画无关, 修复了一个之前的, 关于tangent space裂缝的问题: 引擎设计跟踪( ...
- 引擎设计跟踪(九.14.2j) TableView工具填坑以及多国语言
Blade的UI都是预定义的接口, 然后由插件来负责实现, 目前只有MFC的插件. 最近加上了TableView的视图, 用于一些文件的查看和编辑, 比如前面在文件包的笔记中提到需写一个package ...
- 引擎设计跟踪(九.14.2 final) Inverse Kinematics: CCD 在Blade中的实现
因为工作忙, 好久没有记笔记了, 但是有时候发现还得翻以前的笔记去看, 所以还是尽量记下来备忘. 关于IK, 读了一些paper, 觉得之前翻译的那篇, welman的paper (http://gr ...
- 引擎设计跟踪(九.14.2i) Android GLES 3.0 完善
最近把渲染设备对应的GLES的API填上了. 主要有IRenderDevice/IShader/ITexture/IGraphicsResourceManager/IIndexBuffer/IVert ...
- 引擎设计跟踪(九.14.2f) 最近更新: OpenGL ES & tools
之前骨骼动画的IK暂时放一放, 最近在搞GLES的实现. 之前除了GLES没有实现, Android的代码移植已经完毕: [原]跨平台编程注意事项(三): window 到 android 的 移植 ...
- 引擎设计跟踪(九.14.2d) [翻译] shader的跨平台方案之2014
Origin: http://aras-p.info/blog/2014/03/28/cross-platform-shaders-in-2014/ 简译 translation: 作者在2012年写 ...
- 引擎设计跟踪(九.9) 文件包系统(Game Package System)
很早之前,闪现过写文件包系统的想法, 但是觉得还没有到时候. 由于目前工作上在做android ndk开发, 所以业余时间趁热做了android的移植, 因为android ndk提供的mountab ...
- 引擎设计跟踪(九.14.3.4) mile stone 2 - model和fbx导入的补漏
之前milestone2已经做完的工作, 现在趁有时间记下笔记. 1.设计 这里是指兼容3ds max导出/fbx格式转换等等一系列工作的设计. 最开始, Blade的3dsmax导出插件, 全部代码 ...
- 引擎设计跟踪(九.14.2g) 将GNUMake集成到Visual Studio
最近在做纹理压缩工具, 以及数据包的生成. shader编译已经在vs工程里面了, 使用custom build tool, build命令是调用BladeShaderComplier, 并且每个文件 ...
随机推荐
- php 提示Warning: mysql_fetch_array() expects
我的高度代码如下 include("conn.php"); if(!empty($_GET['id'])){ $sql="select * from ne ...
- 状态可以通过动画切换的按钮--第三方开源--TickPlusDrawable
Android tickplusdrawable(TickPlusDrawable)在github上的项目主页是:https://github.com/flavienlaurent/tickplusd ...
- 关联参数(&的用法)
<?php header("Content-Type:text/html;charset=gb2312"); function test1(&$a){ $a.=&qu ...
- GOOGLE 离线完整安装包下载地址
https://support.google.com/chrome/answer/126299?hl=zh-Hans 官方链接介绍 https://www.google.com/chrome/brow ...
- 在mac上安装pydev for eclipse时,在eclipse的Preferences中无法显示出来的解决方法
参考http://pydev.org/manual_101_install.html 中的说明,该插件依赖java7,在我安装eclipse之前并没有安装jdk,打开eclipse之后,自动安装了一个 ...
- rails 学习笔记
bundle package #保存gem到 vendor/cache bundle install –local 从cache从安装 升级rails bundle config –delete ...
- ios开发笔记
@IBDesignable 可在第二视图中实时预览 @IBInspectable 可编辑属性
- 菜鸟学习Hibernate——缓存
Hibernate的缓存分为三种:一级缓存.二级缓存.查询缓存.下面我就为大家介绍一下. 一.概念. 一级缓存:第一级存放于session中称为一级缓存.Session 级别的缓存,它同session ...
- Charles移动端抓包工具使用
软件Charle 是一个HTTP代理服务器,HTTP监视器,反转代理服务器.它允许一个开发者查看所有连接互联网的HTTP通信.这些包括request, response现HTTP headers (包 ...
- Web端服务器推送技术原理分析及dwr框架简单的使用
1 背景 “服务器推送技术”(ServerPushing)是最近Web技术中最热门的一个流行术语.它是继“Ajax”之后又一个倍受追捧的Web技术.“服务器推送技术”最近的流行跟“Ajax ”有着密切 ...