从渲染管线学 GLES2.0(四)----图元装配

本来这一节应该讲的是裁剪,但是裁剪的时候,根据不同的图元类型处理裁剪的方式也是不一样的,所以在裁剪之前先来说一下图元装配。

1、图元装配

图元装配发生在顶点着色之后,根据我们在调用 glDrawArrays 或者 glDrawElements 时设置的图元类型,将顶点着色输出的顶点组合成指定的图元形状,OpenGL ES 2.0 一共支持 7 种不同的图元类型,经过图元装配之后,图元的类型就只有三种了,分别是点、线、三角形。

2、图元类型

图元枚举值和对应图元类型如下表所示

枚举值

图元类型

GL_POINT

GL_LINES

线

GL_LINE_STRIP

条带线

GL_LINE_LOOP

循环线

GL_TRIANGLES

三角形

GL_TRIANGL_STRIP

条带三角形

GL_TRIANGLE_FAN

扇面三角形

点可以通过单一的顶点来表示,一个点也就是一个四维的其次坐标值。默认的点大小是 1.0,因此当我们渲染点的时候,每个顶点实际上都是屏幕上的一个像素,如果点的大小增加,那么每个点的顶点都会占据超过 1 个像素的值。

点精灵

当点大小是 1 的时候,看起来就是屏幕上的一个像素,当点大小超过 1 的时候,比如设置为 4,那么就是屏幕上的 4x4 大小的一个块,在这个 4x4 块里面的像素除了正常的像素坐标以外,还有一个点精灵坐标,它的范围是 [0, 1],左上角的坐标是 (0, 0),右下角的坐标是 (1, 1),OpenGL 在片段着色器里面提供了一个内置变量来表示这个坐标,它叫做 gl_PointCoord,它的值只对点渲染有效,我们可以使用这个坐标将一个方形的点绘制成一个圆形的点,也可以将 gl_PointCoord 作为输入的纹理坐标使用,那么就可以使用纹理颜色来替代方块的颜色。

使用点精灵绘制圆形的点(片段着色器)

precision mediump float;
void main() {
    vec2 temp = gl_PointCoord - vec2(0.5);
    float dis = dot(temp, temp);
    if(dis > 0.25) {
        discard;
    }
    gl_FragColor = vec4(1.0, 0.9, 0.3, 1.0);
}

绘制效果 

使用点精灵作为纹理坐标(片段着色器)

precision mediump float;
uniform sampler2D vTexture;
void main() {
    gl_FragColor = texture2D(vTexture, gl_PointCoord);
}

绘制效果

线、条带线、循环线

线

两个点表示一条线

条带线

每两个点表示一条线,但是下一条线段的起点是上一条线的结束点

循环线

和条带类似,但是最后一条线段是尾和首相连,形成一个闭环

三角形、条带三角形、扇面三角形

三角形

三个点构成一个三角形

条带三角形

和线的条带有点类似,区别是一个三角形前两个点是上一个三角形的后两个点

扇面三角形

当绘制扇面的时候,第一个顶点会作为一个共享点存在,也是后面每一个三角形的第一个点,然后每一个三角形的最后一个点作为下一个三角形的第二个点存在

这里要特别注意的是 GL_TRIANGL_STRIP 也就是条带三角形,比如我们输入的顶点顺序是 {1,2,3,4,5,6},然后设置图元类型为 GL_TRIANGL_STRIP,那么它的组合方式如下

三角形编号

顶点组成

1

1,2,3

2

3,2,4

3

3,4,5

4

5,4,6

这里需要注意的是第二个和第四个三角形的顶点顺序,顶点顺序不一样计算出来的前后面也是不同的,那么这里就可以写成下面这样

n 为奇数三角形顺序为 n,n+1,n+2 n 为偶数三角形顺序为 n+1,n,n+2

那么对应的代码实现就像下面这样

struct Point {
    float x;
    float y;
};
Point traingles[3];
int index = 0;
bool flip = true;
// 顶点是按照顺序下发的,不是一次性所有的顶点都会到图元装配模块
void PushData(Point point) {
    traingles[index++] = point;
    if (index == 3) {
        std::cout << traingles[0] << " " << traingles[1] << " " << traingles[2] << std::endl;
        if (flip) {
            traingles[0] = traingles[2];
        } else {
            traingles[1] = traingles[2];
        }
        flip = !flip;
        index = 2;
    }
}

3、C++ 代码实现

下面使用一段完整的 C++ 代码来实现图元装配的功能

#define GL_POINTS                         0x0000
#define GL_LINES                          0x0001
#define GL_LINE_LOOP                      0x0002
#define GL_LINE_STRIP                     0x0003
#define GL_TRIANGLES                      0x0004
#define GL_TRIANGLE_STRIP                 0x0005
#define GL_TRIANGLE_FAN                   0x0006

struct Point {
    float x{};
    float y{};
    Point(float x = 0, float y = 0) {
        this->x = x;
        this->y = y;
    }
};

class PrimitiveAssembly {
public:
    PrimitiveAssembly() {}
    ~PrimitiveAssembly() {}

    void SetPrimitiveType(int type) {
        if (primitive_type_ != type) {
            primitive_type_ = type;
        }
    }

    void PushData(const Point& point, bool is_end) {
        if (primitive_type_ == GL_POINTS) {
            std::cout << point.x << " " << point.y << std::endl;
            return;
        }
        recv_point_count_++;
        points_[recv_point_count_ - 1] = point;
        if (primitive_type_ == GL_LINES || primitive_type_ == GL_LINE_STRIP || primitive_type_ == GL_LINE_LOOP) {
            if (recv_point_count_ == 2) {
                HandleLine(is_end);
            }
        } else {
            if (recv_point_count_ == 3) {
                HandleTriangle(is_end);
            }
        }
        if (is_end) {
            recv_point_count_ = 0;
            triangle_strip_flip_ = true;
        }
    }

    void HandleLine(bool is_end) {
        for (int i = 0; i < 2; ++i) {
            std::cout << points_[i].x << " " << points_[i].y << std::endl;
        }
        if (primitive_type_ == GL_LINES) {
            recv_point_count_ = 0;
            return;
        }
        if (primitive_type_ == GL_LINE_LOOP) {
            if (is_end && !line_loop_first_) {
                std::cout << points_[1].x << " " << points_[1].y << std::endl;
                std::cout << tmp_point_.x << " " << tmp_point_.y << std::endl;
                line_loop_first_ = true;
            }
            if (line_loop_first_) {
                tmp_point_ = points_[0];
                line_loop_first_ = false;
            }
        }
        points_[0] = points_[1];
        recv_point_count_ = 1;
    }

    void HandleTriangle(bool is_end) {
        for (int i = 0; i < 3; ++i) {
            std::cout << points_[i].x << " " << points_[i].y << std::endl;
        }
        if (primitive_type_ == GL_TRIANGLES) {
            recv_point_count_ = 0;
            return;
        }
        if (primitive_type_ == GL_TRIANGLE_STRIP) {
            if (triangle_strip_flip_) {
                points_[0] = points_[2];
            } else {
                points_[1] = points_[2];
            }
            triangle_strip_flip_ = !triangle_strip_flip_;
        } else {
            points_[1] = points_[2];
        }
        recv_point_count_ = 2;
    }

private:
    int primitive_type_{};
    int recv_point_count_{};
    Point points_[3];
    Point tmp_point_{};
    bool triangle_strip_flip_{ true };
    bool line_loop_first_{ true };
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值