• 基于OpenGL编写一个简易的2D渲染框架-13 使用例子


     

    这是重构渲染器的最后一部分了,将会给出一个 demo,测试模板测试、裁剪测试、半透明排序等等:

    上图是本次 demo 的效果图,中间的绿色图形展现的是模板测试。

    模板测试

    void init(Pass*& p1, Pass*& p2)
    {
        p1 = new Pass;
        p2 = new Pass;
    
        Shader* s1 = new Shader("Shader/defaultGeometryShader.vs", "Shader/defaultGeometryShader.frag", CVA_V3F_C4F);
        Shader* s2 = new Shader("Shader/defaultGeometryShader.vs", "Shader/defaultGeometryShader.frag", CVA_V3F_C4F);
    
        p1->enableBlend(true);
        p2->enableBlend(true);
    
        p1->setBlendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA, BLEND_ONE, BLEND_ZERO);
        p2->setBlendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA, BLEND_ONE, BLEND_ZERO);
    
        p1->setShader(s1);
        p2->setShader(s2);
    
        p1->setPrimType(PT_TRIANGLES);
        p2->setPrimType(PT_TRIANGLES);
    
        p1->enableStencilTest(true);
        p1->setStencilMask(0xFF);
        p1->setStencilCompareFunc(COMPARE_ALWAYS);
        p1->setStencilRef(1);
        p1->setStencilOp(STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_REPLACE);
    
        p2->enableStencilTest(true);
        p2->setStencilMask(0xFF);
        p2->setStencilCompareFunc(COMPARE_EQUAL);
        p2->setStencilRef(1);
        p2->setStencilOp(STENCIL_OP_KEEP, STENCIL_OP_KEEP, STENCIL_OP_REPLACE);
    }

    使用模板测试需要两个 Pass,第一个 Pass 绘制圆的时候,把圆范围内的模板值设置为 1,。在时候 p1 绘制好圆后,再使用第二个 pass 绘制两个波纹效果的图形,这时的 pass 设置比较函数为等于,即只有模板值等于 1 的像素才不会被抛弃:

    /* 模板测试 */
    void stencilTest(GraphicsContext* gc, Canvas2D* canvas, Pass* p1, Pass* p2)
    {
        static float d = 0;
        static float d1 = 0;
        static float h = 0;
    
        Vec2 vs1[23];
        Vec2 vs2[23];
    
        gc->render();
        canvas->setCustomPass(p1);
        canvas->fillCircle(Vec3(400, 300, 0), 100, 360, Color(1, 1, 1, 0));
        gc->render();
    
        canvas->setCustomPass(p2);
    
        float hz = 20;
        vs1[0].set(500, 200);
        vs1[1].set(300, 200);
        for ( int i = 0; i <= hz; i++ ) {
            float y = sinf(i / hz * PI_2 + d + i / hz * 2);
    
            vs1[i + 2].set(300 + i / hz * 200, y * 15 + 200 + h);
        }
        canvas->fillPath(vs1, 23, Color(0, 1, 0, 0.5));
    
        
        vs2[0].set(500, 200);
        vs2[1].set(300, 200);
        for ( int i = 0; i <= hz; i++ ) {
            float y = sinf(i / hz * PI_2 + d1 + i / hz * 3);
    
            vs2[i + 2].set(300 + i / hz * 200, y * (10 + i / hz * 10) + 200 + h);
        }
        canvas->fillPath(vs2, 23, Color(0, 1, 0, 0.7));
    
        gc->render();
        canvas->setCustomPass(nullptr);
    
        h += 0.08;
        if ( h > 200 ) {
            h = 0;
        }
    
        d += 0.01;
        if ( d >= PI_2 ) d = 0;
        d1 += 0.02;
        if ( d1 >= PI_2 ) d1 = 0;
    }

    最终的效果:

    和迅雷的悬浮球显示下载进度的效果相差不多。

    裁剪测试

    在使用裁剪测试时,使用一种粒子效果作为测试对象。粒子会拖出一条长长的尾巴,碰到窗口边缘时反弹。四条绿线围成的矩形为裁剪区域,粒子在矩形区域外的部分不会被显示出来。代码实现:

        ParticleSystem* ball = new ParticleSystem();
        ball->initWithPlist("Particle/motion.plist");
        ball->setTexture("Particle/fire.png");
        ball->getEmitter()->setEmitPos(Vec2(400, 300));
    
        Pass* pass = ball->getPass();
        pass->enableScissor(true);
        pass->setScissorRect(100, 100, 600, 400);
    
        ParticleSystemManager manager;
        manager.appendParticleSystem(ball);

    先创建一个粒子系统,实现拖尾的粒子效果。然后获取粒子系统的 Pass 对象(每个粒子系统都会有一个 pass 对象),开启裁剪测试,随机给出一个裁剪区域,初始化到此结束。

    /* 裁剪测试 */
    void scissorTest(ParticleSystem* ps, Canvas2D* canvas)
    {
        static float x = 400, y = 300;
        static int xdir = 1, ydir = 1;
        static float clipx1 = 0, clipy1 = 0, clipx2 = 800, clipy2 = 600;
        static int clipdx = 1, clipdy = 1;
    
        ps->getEmitter()->setEmitPos(Vec2(x, y));
        int speed = 2.5;
        x -= xdir * speed;
        y -= ydir * speed;
        if ( x < 0 ) {
            xdir = -1;
        }
        else if ( x > DEFAULT_WIN_W ) {
            xdir = 1;
        }
        if ( y < 0 ) {
            ydir = -1;
        }
        else if ( y > DEFAULT_WIN_H ) {
            ydir = 1;
        }
    
        clipx1 += clipdx * 0.5f;
        clipx2 -= clipdx * 0.5f;
        clipy1 += clipdy * 0.5f;
        clipy2 -= clipdy * 0.5f;
        if ( clipx1 >= 150 ) {
            clipdx = -1;
        }
        else if ( clipx1 <= 0 ) {
            clipdx = 1;
        }
        if ( clipy1 >= 150 ) {
            clipdy = -1;
        }
        else if ( clipy1 <= 0 ) {
            clipdy = 1;
        }
        canvas->drawLine(0, clipy1, DEFAULT_WIN_W, clipy1, Color(0, 1, 0, 1));
        canvas->drawLine(clipx1, 0, clipx1, DEFAULT_WIN_H, Color(0, 1, 0, 1));
    
        canvas->drawLine(0, clipy2, DEFAULT_WIN_W, clipy2, Color(0, 1, 0, 1));
        canvas->drawLine(clipx2, 0, clipx2, DEFAULT_WIN_H, Color(0, 1, 0, 1));
        ps->getPass()->setScissorRect(clipx1, clipy1, clipx2 - clipx1, clipy2 - clipy1);
    }

    每一帧,都移动四条绿线,实现裁剪区域的变化。在改变裁剪区域后,要更新 pass 的裁剪区域。作为对比,还加入了火焰的粒子效果(没有对 pass 做任何的更改),火焰跟随鼠标的位置移动。

    半透明图形排序

    这个主要在绘制图形的时候设置深度值即可:

            for ( int i = 0; i < 10; i++ ) {
                int x = 20 + i * 20;
                int y = 20 + i * 20;
                if ( i < 5 ) {
                    canvas.fillRect(x, y, x + 100, y + 100, Color(0.2 * i, 0, 0, 0.1 + 0.1 * i), i);
                }
                else {
                    canvas.fillRect(x, y, x + 100, y + 100, Color(0, 0, 0.2 * i, 0.1 * i), i);
                }
            }

    fillRect 函数的最后一个参数就是深度值(渲染器中就是根据深度值 depth 进行排序的),数值小的先被绘制。

  • 相关阅读:
    Oracle创建自增字段方法-ORACLE SEQUENCE的简介
    iOS项目开发实战——使用Xcode6设计自己定义控件与图形
    准备开源用javascript写Tomcat下的WebApp的项目
    Codeforces Round #256 (Div. 2) B. Suffix Structures
    静默方式安装10g数据库软件+升级patch+手工建库
    oracle 数据库开发面试题
    待机异常篇
    HTTP状态码(HTTP Status Code)
    POJ3126——Prime Path
    RHEL7 -- 通过gerp使用正则表达式
  • 原文地址:https://www.cnblogs.com/ForEmail5/p/7112638.html
Copyright © 2020-2023  润新知