Blade引擎的shader/FX系统
相对来说非常简单,使用XML定义材质:technique、pass,包含renderstates和自定义的semantic 。shader目前用hlsl,受到doxygen的启发,将引擎自定义的部分写在注释部分,这样可以保证hlsl可以不做任何预处理而被正常编译。注释部分(//!)会被单独提取出来作为一个ini被parse。贴一个VS的sample吧。
1 //!BladeShaderHeader 2 //![VertexShader] 3 //!Entry=TerrainVSMain 4 //!Profile=vs_3_0 5 //![FragmentShader] 6 //!Entry=TerrainPSMain 7 //!Profile=ps_3_0 8 9 #include "inc/light.hlsl" 10 #include "inc/common.hlsl" 11 #include "inc/terrain_common.hlsl" 12 13 //![Semantics] 14 //!wvp_matrix = WORLD_VIEWPROJ_MATRIX 15 //!world_translate = WORLD_POSITION 16 17 18 void TerrainVSMain( 19 float2 hpos : POSITION0, 20 float2 vpos : POSITION1, 21 float4 normal : NORMAL0, //ubyte4-n normal 22 23 uniform float4x4 wvp_matrix, 24 uniform float4 world_translate, 25 uniform float4 scaleFactor, //scale 26 uniform float4 UVInfo, //uv information 27 28 out float4 outPos : POSITION, 29 out float4 outUV : TEXCOORD0, 30 out float4 outBlendUV : TEXCOORD1, 31 out float3 outWorldPos : TEXCOORD2, 32 out float3 outWorldNormal : TEXCOORD3 33 ) 34 { 35 float4 pos = float4(hpos.x, getMorphHeight(vpos, hpos+world_translate.xz, eye_position.xz), hpos.y, 1); 36 pos = pos*scaleFactor; 37 38 float blendOffset = UVInfo[0]; 39 float tileSize = UVInfo[1]; 40 float blockSize = UVInfo[2]; 41 float blockUVMultiple = UVInfo[3]; 42 43 //normalUV 44 outUV.xy = pos.xz*(tileSize-1)/(tileSize*tileSize) + 0.5/tileSize; 45 //block repeat UV 46 outUV.zw = pos.xz*blockUVMultiple/blockSize; 47 //blendUV 48 outBlendUV.xy = pos.xz*(tileSize-1)/(tileSize*tileSize) + blendOffset/tileSize; 49 outBlendUV.zw = pos.xz/tileSize; 50 51 //use local normal as world normal, because our terrain has no scale/rotations 52 outWorldNormal = expand_vector(normal).xyz; //ubytes4 normal ranges 0-1, need convert to [-1,1] 53 54 //don't use full transform because our terrain has no scale/rotation 55 outWorldPos = pos.xyz+world_translate.xyz; 56 57 outPos = mul(pos, wvp_matrix); 58 }
目前支持自定义的semantic,不支持annotation。由于没有使用d3dx的FX框架,而dx9的shader不支持自定义的senmatic,所以没有使用DX11的风格,而是写在注释里。senmatic可以写在shader注释里面,也可以写在材质脚本(XML)里面,两种都可以。
关于shader cosntant的优化。很早在OpenGPU提问过。首先shader constant可以分为global和per-instance。
global的就是在同一帧中不会变化的,比如light(DS),camera pos,view和projection, time 等等。
per-instance的通常是跟每个对象有关,比如world matrix, world_view_projection, instance color等等。
对于DX和GL, 对于所有的shader, 可以将global constant都使用相同的寄存器来保存,比如(c0), 这样在同一帧, 这些数据只需要更新一次。由于使用相同的寄存器,同一帧内后续的绘制不需要更新这些变量。
不过目前好像GLES2.0还不支持指定寄存器索引。
这种方式Blade还没有使用,后期优化会考虑,一开始已经将这两种变量类型的update分开,所以优化起来不难。
另外DX9也可以一次性更新一组变量,以减少SetShaderConstant的调用次数, 比如OGRE中就有,不过Blade还没有做。这个应该不算太难,只要render device(引擎的render API抽象层)内部维护一个CPU constant array,用来批量update就可以了。猜测与DX11相差应该不大。比如DX11可能使用两个或者多个constant buffer,将同一帧内频繁修改的cbuffer与不频繁的分开。由于还没用过dx11,所以不瞎猜了。
渲染管线(render pipeline)与空间管理(space management)
既然提到render API抽象层,再简单说下引擎的graphics system的组织。由于细节太多,就简单说一下。
render device (render API abstraction:dx9,dx11,gl,gles)
|
|
render piple line (FX framework, render queue) ---- camera、view ---- space (scene management/ space partition)
render device 是对渲染状态设置和draw call的抽象,它与render target、texture, vertex、index buffer, shader等组成了render API抽象层。这层抽象位于foundation library,只有抽象,有专门的device library来实现对应的API。
space主要负责图形scene management、culling,通过预定义的接口,可以有具体的实现方案。比如可以是quadtree,octree, 也可以是no space partition(最简模式),甚至(partition+)Software Occlusion。
render pipline 主要是渲染的组织,FX框架的runtime 和 render queue的sorting等,包含了各种phase,每个phase可以有自己的input buffer和output buffer。
借鉴了nebula引擎,一个pipeline是可配置的,下面是forward shading 的配置。目前pipeline runtime和配置系统还不完善。
使用配置文件的目的是保证管线足够灵活,并且期望其支持FS、DS、DL, 并能方便集成各种PostEffect等等。
1 <?xml version="1.0" encoding="utf-8"?> 2 3 <!-- technique is the technique name for all render types' materials --> 4 <render_scheme name="Forward Shading" technique="default" > 5 6 <!-- render buffer definition, the default final output is not needed, as it is controlled by program --> 7 <!-- pre-defined buffers are "FINAL", "EMPTY" --> 8 <render_buffers> 9 <!-- FINAL color buffer : the final buffer created by app. it doesn't have any depthstencil buffer attached --> 10 <!-- 11 attributes: 12 width/height: set the target dimension. example "1280" or "50%" or "50%+256". 13 "P%" means the dimension is decided relatively to the size of final buffer. 14 format: the pixel format that to be used. 15 type: buffer type: either to be "color" or "depth" 16 access: graphics device access. could be "read", "write", or "read_write" 17 18 --> 19 <buffer name="depth_buffer" width="100%" height="100%" type="depth" format="D24S8" /> 20 </render_buffers> 21 22 <!-- types definition --> 23 <render_types> 24 <type name="Terrain"/> 25 <type name="Static Mesh"/> 26 <type name="Skinned Mesh"/> 27 <type name="Atmosphere"/> 28 <type name="Effect"/> 29 <type name="AABB"/> 30 <type name="Gizmo Helper"/> 31 </render_types> 32 33 <!-- pre-z pass --> 34 35 <!-- shadow pass --> 36 37 <!-- foward pass --> 38 <render_output name="Forward Shading" target="FINAL" target_depth="depth_buffer" enable="true" > 39 40 <!-- view definition available options are : 41 reference = 42 "FINAL" - use the app specific view, 43 "NONE" - default value. use a new view object , with its default values already being set properly 44 45 or create new view with attributes as follows 46 <view [reference="NONE"] left="10+20T" top="5+10%" right="20+10%" bottom="100%"> 47 <clear color="NONE / (0,0,0,0)" depth="NONE / 1.0f" stencil="NONE / <uint16_val>" /> 48 </view> 49 --> 50 <view reference="FINAL" /> 51 52 <!-- camera definition 53 "reference" options are: 54 "MAIN" - use the app speicified main camera 55 "MAIN_LIGHT" - use the main directional light bounded camera 56 "$NAME" - use the camera which has the name "$NAME", created by app 57 unlike view object, camera must have a reference, to use the reference camera's space geometry data (rotation/position) 58 but it can be customized. 59 60 attributes: 61 custom: enable customized attributes. use "disable" to disable the following custom attributes, "enable" to enable them. default value: "disable" 62 custom attributes: 63 type: projection mode. PERSPECTIVE/ORTHOGRAPHIC. default value: same as referenced target. 64 near/far: near/far clip plane. default value: same as referenced target. 65 horiz_fov: horizontal field of view. default vallue: same as referenced target. 66 aspect: aspect ratio. default vallue: same as referenced target. 67 width/height: project size in orthographic mode. P% means using value relative view' width/height. 68 "default" of width/height equals 100%. default vallue: same as referenced target. 69 70 note: custom camera params does not affect scene/space culling, it is just used for rendering only 71 example: 72 <camera reference="MAIN" custom="enable/disable" static="disable" type="PERSPECTIVE" horiz_fov="90" /> 73 74 --> 75 <camera reference="MAIN" /> 76 77 <render_step type="Atmosphere" enable="true" pass="default" /> 78 79 <render_step type="Terrain" enable="true" pass="default" /> 80 81 <render_step type="Static Mesh" enable="true" pass="default" /> 82 83 <render_step type="Effect" enable="true" pass="default"/> 84 85 <render_step type="AABB" enable="true" pass="default"/> 86 87 <render_step type="Gizmo Helper" enable="true" pass="default" /> 88 </render_output> 89 90 <!-- !if you want draw a single object, 91 then you'd better put the object and the referenced camera into a separated space, 92 so that this object will not be affetcting or affected by the rest of the entire scene --> 93 94 <!-- !important: better to put 2D objects in a spearated space --> 95 <render_output name="2D" target="FINAL" target_depth="depth_buffer" enable="true" > 96 97 <view reference="NONE"> 98 <!-- default value for clear: no clear - color=”NONE“, depth="NONE" --> 99 <clear/> 100 </view> 101 102 <camera type="ORTHOGRAPHIC" custom="enable" near="0.1" far="1000" /> 103 </render_output> 104 105 </render_scheme>