美文网首页
Urho3D 1.7.1 源代码分析(三)

Urho3D 1.7.1 源代码分析(三)

作者: RonZheng2010 | 来源:发表于2019-11-30 21:35 被阅读0次

    1. Application

    这里的分析主要参考Urho3D的两个例子HelloWorld和StaticScene,前者使用UI组件,用于分析UI子系统,后者显示模型,用于分析Renderer子系统。

    1.1 URHO3D_DEFINE_APPLICATION_MAIN()

    URHO3D_DEFINE_APPLICATION_MAIN()宏用于创建唯一的application实例。下面是Urho3D的例子HelloWorld。

    URHO3D_DEFINE_APPLICATION_MAIN(HelloWorld)
    

    展开后的代码如下:

    int RunApplication() 
    { 
        Urho3D::SharedPtr<Urho3D::Context> context(new Urho3D::Context()); 
        Urho3D::SharedPtr<className> application(new HelloWorld(context)); 
        return application->Run(); 
    } 
    
    int main(int argc, char** argv) 
    { 
        Urho3D::ParseArguments(argc, argv); 
        return RunApplication(); 
    }
    

    HelloWorld从Application派生,这里创建HelloWorld的实例,并调用Application::Run()启动它。在创建application之前,先创建公共的Context实例,以便传给Application,并在之后再传给其他类。

    1.2 定制Application

    Application定义了一组虚拟函数Setup()、Start()和Stop()。使用者可以在自己的Applicatio类中定制自己的实现。一个典型的做法是在Start()中创建UI组件和模型组件。这样UI子系统和Renderer系统可以分别对它们进行操作。

    下图是main()的调用步骤。

    • 创建Context实例。
    • 创建使用者的Application类实例。
    • 在Application::Run()中调用使用者实现的Setup()、Start()和Stop()
    • 在Application()中创建Engine实例,在Application::Run()调用Engine::Initialize()初始化,在调用Engine::RunFrame()驱动它。Engine组合了对UI和Renderer及其他子系统的功能。

    2. Engine

    2.1 Engine::Engine()

    在构造函数Engine()中,初始化UI、FileSystem、Log、ResourceCache等子系统并向Context注册。在UI的构造函数UI()中,调用RegisterUILibrary()。

    2.2 Engine::Initialize()

    Engine::Initialize()的工作如下:

    • 创建Graphics子系统和Renderer子系统,向Context注册。

    • 调用Graphics::SetMode(),这将触发UI子系统和Renderer子系统的初始化。

      • 调用SDL_CreateWindow()创建用于绘制的窗口。
      • 发送E_SCREENMODE事件。UI在UI::HandleScreenMode()中处理该事件,调用Renderer::Initialize()。Renderer在Renderer::HandleScreenMode()中处理该事件,调用Renderer::Initialize()。
      • 调用CheckFeatureSupport(),检查支持哪些OpenGL扩展选项。
    • 设置Renderer。

    2.3 Engine::runFrame()

    Engine::RunFrame()依次调用Engine::Update()进行渲染前的准备工作,然后调用Engine::Render()渲染。

    对于UI子系统,

    • 发送的E_POSTUPDATE事件,由UI::HandlePostUpdate()处理,后者调用UI::Update()。
    • 发送的E_RENDERUPDATE事件,由UI::HandleRenderUpdate()处理,其中调用UI::RenderUpdate()。
    • 在Engine::Render()中,调用UI::Render()渲染UI组件。

    对于Renderer子系统,

    • 发送的E_RENDERUPDATE事件,由Renderer::HandleRenderUpdate()处理,其中调用Renderer::Update()。
    • 在Engine::Render()中,调用Renderer::Render()渲染模型。

    Engine中还包括Graphics的处理,Graphics负责绘制图像前的准备工作。

    在Graphics::BeginFrame()中,

    • 如调用glEnable()设置绘制选项。
    • 调用glViewPort()设置绘制视口。
    • 发送E_BEGINRENDERING事件。

    在Graphics::EndFrame()中,

    • 发送E_ENDRENDERING事件。

    3. Graphics子系统

    Graphics包括与显示编程接口有关的操作。如下列出了Graphics的部分成员函数。

    函数 功能 调用的函数
    SetMode() 创建窗口 SDL_CreateWindow()
    Clear() 清除背景 glClearColor()、glClearDepth()、glClearStencil()、glClear()
    SetViewport() 设置视口 glViewport()
    SetBlendMode() 设置混合模式 glBlendFunc()、glBlendEquation()
    SetColorWrite() 设置颜色mask glColorMask()
    SetCullMode() 设置剔除模式 glCullFace()
    SetDepthTest() 设置深度测试 glDepthFunc()
    SetScissorTest() 设置裁剪测试 glScissor()
    SetStencilTest() 设置模板测试 glStencilFunc()、glStencilOp()
    SetShaders() 设置Shader、Program glCreateShader()、glShaderSource()、glCompileShader()、
    glCreateProgram()、glAttachShader()、glLinkProgram、glUseProgram()
    SetShaderParameter() 设置Shader的uniform变量 glUniform1fv()、glUniformMatrix3fv() ...
    SetVBO() 绑定顶点buffer glBindBuffer()
    PrepareDraw() 设置顶点数据 glVertexAttribPointer()
    Draw() 绘制 glDrawArrays()、glDrawElements()、glDrawElementsInstanced()

    3.1 Shader & Program

    3.1.1 Shader & Program概念

    Shader是从资源文件加载的Resource,而ShaderVariation是从Shader生成的、OpenGL意义上的Shader。ShaderVariation的成员owner_指向引用的Shader实例。另外一个成员type_指出这是一个vertex shader还是一个pixel/fragment shader。

    ShaderProgram类代表program。在构造函数中,它引用一个vertex ShaderVariation和一个pixel ShaderVariation。

    ShaderProgram的成员vertexAttributes_保存了program的vertex attribute到其位置的映射关系。Vertex attribute的信息包括两部分,一是semantic,二是序号。所谓的semantic由数组ShaderVariation::elementSemanticNames[]指定:

    const char* ShaderVariation::elementSemanticNames[] =
    {
         "POS",
         "NORMAL",
         "TEXCOORD",
    };
    

    如下是一个vertex attribute的例子。

    attribute vec4 iPos;
    attribute vec3 iNormal;
    attribute vec4 iCubeTexCoord1;
    

    通过检查attribute的名字是否包含elementSemanticNames[]中的semantic字符串,判断是那种semantic。如iPos包含POS,所以对应的semantic是POS。

    Attribute可能附加了数字,如iCubeTexCoord1附加了1,这个数字就是序号。如果没有附加数字,则序号为-1。如iCubeTexCoord1的序号是1,iPos的序号是-1。

    ShaderParameter保存了uniform变量的信息,type_是从属shader的类型,name_是变量名,location_是变量位置。ShaderProgram的成员shaderParameters_则保存了一般uniform变量(不包括sampler变量)的名称到ShaderParameter的映射关系。

    3.1.2 创建Shader

    ShaderVariation::Create()负责创建opengl意义上的shader。

    • 调用Shader::GetSourceCode()得到Shader源代码。
    • 源代码中使用了宏,以便指定其中某些代码有效或无效。使用ShaderVariantion::SetDefines()可以设置这些宏。下面的DIFFMAP是宏的一个例子。如果定义了DIFFMAP,则往代码中加入#define DIFFMAP,就是下面红色的一行。
    #define DIFFMAP
    
    void VS()
    {
         #ifdef DIFFMAP
             vTexCoord = iTexCoord;
         #endif
    }
    

    指定的宏不同,则从一个Shader实例创建的ShaderVariation不同。如果宏相同,就要避免重复创建ShaderVariation实例了。Shader把从宏到ShaderVariation实例的映射保存在成员vsVariantions_和psVariantions_中。

    调用Shader::GetVariation()可以得到与指定宏对应的ShaderVariation,它的参数就是包含宏的字符串。

    • NormalDefines()将字符串标准化,也就是把字符全部转成大写,将包含的多个宏重新按顺序排列。
    • 如果对应的ShaderVariation还不存在,则创建ShaderVariation实例,并设置宏。后面调用ShaderVariation::Create()时就可以使用了。
    • 如果ShaderVariation已经存在,则返回就好了。

    3.1.3 创建Program

    ShaderProgram::Link()创建Program,分三步。

    一是调用glCreateProgram()和glAttachShader()创建program。

    二是得到vertex attribute。

    • 调用glGetProgramiv(GL_ACTIVE_ATTRIBUTES)得到所有vertex attribute,并遍历。
    • 调用glActiveAttrib()得到vertex attribute。调用glGetAttribLocation()得到attribute的位置。根据vertex attribute的名字查询ShaderVariation::elementSemanticNames[]数组,得到其semantic。将attribute的信息(samantic, 序号)到位置的映射关系保存在成员vertexAttributes_中。

    三是得到uniform变量,包括sampler变量和一般的uniform变量。区分这两者办法是看前缀字符。sampler变量以s开头,如sDiffMap、sDiffCubeMap等。一般变量以c开头,如cModel、cViewProj等。

    • 调用glGetProgramiv(GL_ACTIVE_UNIFORMS)得到所有vertex attribute,并遍历。
    • 对于sampler变量,找到与变量对应的texture unit,调用glUniform1iv()将uniform变量绑定到texture unit上。
    • 对于一般变量,将变量名和ShaderParameter的映射关系保存在成员shaderParameters_中。

    3.1.4 Graphics与ShaderProgram

    Graphics的成员impl_是一个GraphicsImpl实例,后者分担Graphics的部分功能。

    GraphicsImpl的成员shaderProgram_持有唯一的ShaderProgram实例。

    3.2 VertexBuffer

    3.2.1 Vertex Buffer概念

    GPUObject保存OpenGL对象的句柄。它的成员object_是一个union类型GPUObjectHandle,后者的成员name_保存的就是句柄值,如glGenBuffers()得到的buffer的句柄值。

    VertexBuffer从GPUObject派生的,它保存一组顶点。VertexBuffer的每个顶点可以包括不同semantic的顶点数据,semantic由枚举类型VertexElementSemantic规定,如位置、法线向量等。

    enum VertexElementSemantic
    {
        SEM_POSITION = 0,
        SEM_NORMAL,
        SEM_TEXCOORD,
    };
    

    VertexElement负责保存这些semantic信息。VertexBuffer的成员elements_就是一组VertexElement实例,如果VertexBuffer中有3种semantic数据,则elements_中就有3个VertexElement实例。

    Vertexbuffer中数据的排列方式是每个顶点的数据放在一起,然后所有顶点排列成一个数组。

    VertexElement除了semantic_之外的其他成员:

    • type_是数据类型,可以通过查询数组ELEMENT_TYPESIZE[]得到它的字段大小。
    URHO3D_API const unsigned ELEMENT_TYPESIZES[] =
    {
        sizeof(int),
        sizeof(float),
    }
    
    • offset_是该semantic在vertexbuffer的数据中的偏移位置。

    3.2.2 VertexBuffer::SetSize()

    有两层VertexBuffer::SetSize()。

    • 顶层SetSize()的参数是一个位掩码,指定有哪些vertex element。调用GetElements(),在数组LEGACY_VERTEXELEMENTS[]查找每个位掩码对应的VertexElement,并保存在VertexBuffer的成员elements_中。注意这时的VertexElement只有数据类型和semantic,而offset_等其他成员没有设置。
    extern URHO3D_API const VertexElement LEGACY_VERTEXELEMENTS[] =
    {
        VertexElement(TYPE_VECTOR3, SEM_POSITION, 0, false),     // Position
        VertexElement(TYPE_VECTOR3, SEM_NORMAL, 0, false),       // Normal
        VertexElement(TYPE_UBYTE4_NORM, SEM_COLOR, 0, false),    // Color
    }
    
    • 调用下层VertexBuffer::SetSize()。其中,
      • 调用UpdateOffsets()。遍历elements_,设置VertexElement的成员offset_。
      • 调用Create()。其中调用glGenBuffers()生成buffer,调用glBindBuffer()绑定buffer,调用glBufferData()写入顶点数据,不过这时顶点数据为空。

    3.2.3 VertexBuffer::SetData()

    VertexBuffer::SetData()的工作是:

    调用glBindBuffer()绑定Buffer,调用glBufferData()写入顶点数据,这里顶点数据不为空。

    相关链接
    Urho3D 1.7.1 源代码分析 (一)
    Urho3D 1.7.1 源代码分析 (二)
    Urho3D 1.7.1 源代码分析 (三)
    Urho3D 1.7.1 源代码分析 (四)
    Urho3D 1.7.1 源代码分析 (五)

    相关文章

      网友评论

          本文标题:Urho3D 1.7.1 源代码分析(三)

          本文链接:https://www.haomeiwen.com/subject/hslawctx.html