片段着色器

OpenGLES可编程管线流程:


可编程管线流程

一、固定功能片段着色器
在OpenGLES 1.1中(固定功能桌面OpenGL),可以使用一组有限的方程式,确定如何组合片段着色器的不同输入。可以使用三种输入:插值顶点颜色、纹理颜色和常量颜色。顶点颜色通常保存一个预先计算的颜色或顶点照明计算的结果。纹理颜色来自于使用图元纹理坐标绑定的纹理中读取的值,而常量颜色可以对每个纹理单元设置。组合这些输入相当有限,输入的A、B、C可能来自于顶点颜色、纹理颜色或常量颜色,组合能够实现大量有趣的特效,但是比起可编程管线有很大的距离。可用的方程式如下图所示

RGB组合函数

二、片段着色器概述
片段着色器为片段提供了通用功能的可编程方法。输入部分包括:
输入 —— 顶点着色器生成的插值数据
统一变量 —— 片段着色器使用的状态,常量值
采样器 —— 用于访问着色器中的纹理图像
代码 —— 片段着色器源代码或二进制代码,描述在片段上执行的操作

片段着色器的输入输出如下:


片段着色器输入输出

内建特殊变量:
gl_FragCoord —— 片段着色器中的只读变量。保存片段的窗口相对坐标
gl_FrontFacing —— 片段着色器中的一个只读变量。表示片段是否是正面图片的一部分
gl_PointCoord —— 只读变量,渲染点时使用。
gl_FragDepth —— 只写输出变量,在片段着色器中写入时,覆盖片段的固定功能深度值。慎用,因为可能会导致GPU深度优化禁用,即“Early-Z”功能被禁用。“Early-Z”会使得不能通过深度测试的片段不会被着色。

内建常量
gl_MaxFragmentInputVectors —— 片段着色器输入的最大数量,15
gl_MaxTextureImageUints —— 可用纹理图像单元的最大数量,16
gl_MaxFragmentUniformVectors —— 片段着色器内可以使用的vec4统一变量项目的最大数量,224
gl_MaxDrawBuffers —— 多重渲染目标(MRT)的最大支持数量,4
gl_MinProgramTexelOffset/gl_MaxProgramTexelOffset —— 通过内建ESSL函数 texture * Offset() 便宜参数支持的最小和最大偏移量,分别是 -8 和 7
可以通过glGetIntegerv 来查询

精度限定符
片段着色器中没有默认精度,因此每个片段着色器程序都必须声明一个默认精度。

三、多重纹理
多重纹理用于组合多个纹理贴图。下面来介绍一下多重纹理的用法
顶点着色器代码如下:

#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
out vec2 v_texCoord;
void main() {
    gl_Position = a_position;
    v_texCoord = a_texCoord;
}

片段着色器代码如下:

#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_baseMap;
uniform sampler2D s_lightMap;
void main() {
    vec4 baseColor;
    vec4 lightColor;

    baseColor = texture(s_baseMap, v_texCoord);
    lightColor = texture(s_lightMap, v_texCoord);
    outColor = baseColor * (lightColor + 0.25);
}

意思就是将两个texture进行矩阵相乘合并。
加载Texture代码如下:

typedef struct {
    GLuint programObject;

    GLint baseMapLoc;
    GLint lightMapLoc;

    GLuint baseMapTexId;
    GLuint lightMapTexId;

} UserData;


GLuint loadTexture(void *ioContext, char *fileName) {

    int width, height;
    char *buffer = loadTGA(ioContext, fileName, &width, &height);
    GLuint textureId;

    if (buffer == NULL) {
        ALOGE("Error loading (%s) image.\n", fileName);
        return 0;
    }

    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    free(buffer);

    return textureId;
}

上述代码就是生成一个Texture,并加载TGA图片并利用glTexImage2D方法创建一个Texture,设置相应的过滤参数。简化版的TGA图片加载代码如下,下面的代码只保证加载非压缩的TGA图片,不支持采用RLE压缩的TGA图片:

// #pragma pack指定内存对齐方式
#pragma pack(push,x1)
// 这里表示按照1字节对齐方式对齐
#pragma pack(1)
// TGA图片格式,RLE算法
typedef struct {
    unsigned char  IdSize, // 图像信息字段长度, 范围 0 ~ 255, 0表示没有图像的信息字段
            MapType,        // 颜色表类型,0表示没有颜色。
            ImageType;      // 图像类型码,2表示非压缩格式,10表示压缩RGB格式,后面的图像数据采用RLE压缩

    unsigned short PaletteStart,    // 颜色表首地址,颜色表头的入口,整形(低位 —— 高位)
            PaletteSize;            // 颜色表的长度,整形(低位 —— 高位)
    unsigned char  PaletteEntryDepth;   // 颜色表的位数,16表示16位TGA,24表示24位TGA,32表示32位TGA

    unsigned short X,   // 图像X坐标起始位置,左下角的X坐标
            Y,          // 图像Y坐标起始位置,左下角的Y坐标
            Width,      // 图像的宽度
            Height;     // 图像的高度
    unsigned char  ColorDepth,  // 图像没像素存储占用位数
            Descriptor;         // 图像描述符字节

} TGA_HEADER;
#pragma pack(pop,x1)


/**
 * 加载一个 8bit,24bit或32bit TGA图片
 * @param context
 * @param fileName
 * @param width
 * @param height
 * @return
 */
char* loadTGA(void* context, const char *fileName, int *width, int *height) {
    char* buffer;
    AAsset* file;
    TGA_HEADER header;
    int bytes;
    file = fileOpen(context, fileName);
    if (file == nullptr) {
        ALOGE("fialed to load: {%s}\n", fileName);
        return nullptr;
    }

    bytes = fileRead(file, sizeof(TGA_HEADER), &header);
    *width = header.Width;
    *height = header.Height;
    //
    if (header.ColorDepth == 8 || header.ColorDepth == 24 || header.ColorDepth == 32) {
        int bytesToRead = sizeof(char) * (*width) * (*height) * header.ColorDepth / 8;

        buffer = (char *)malloc(bytesToRead);
        if (buffer) {
            bytes = fileRead(file, bytesToRead, buffer);
            fileClose(file);
            return buffer;
        }
    }

    return nullptr;
}

接下来是初始化部分,初始化主要完成加载glsl程序代码,创建program以及绑定统一变量,代码如下:

int init(ESContext *esContext) {
    UserData *userData = (UserData *) esContext->userData;

    char *vertexStr = readAssetFile("vertex_multitexture.glsl",
                                    esContext->activity->assetManager);
    char *fragmentStr = readAssetFile("fragment_multitexture.glsl",
                                      esContext->activity->assetManager);

    userData->programObject = loadProgram(vertexStr, fragmentStr);

    userData->baseMapLoc = glGetUniformLocation(userData->programObject, "s_baseMap");
    userData->lightMapLoc = glGetUniformLocation(userData->programObject, "s_lightMap");

    userData->baseMapTexId = loadTexture(esContext->activity->assetManager, "basemap.tga");
    userData->lightMapTexId = loadTexture(esContext->activity->assetManager, "lightmap.tga");

    if (userData->baseMapTexId == 0 || userData->lightMapTexId == 0) {
        return FALSE;
    }

    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    return TRUE;
}

完成初始化后,便是绘制部分,绘制部分主要完成使能Texture0 和 Texture1,然后使用glUniform1i方法绑定到对应的纹理等功能,代码如下:

void onDraw(ESContext *esContext) {
    UserData *userData = (UserData *) esContext->userData;
    // 顶点和texture
    GLfloat vertices[] = {
            -0.5f, 0.5f, 0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
            0.5f, -0.5f, 0.0f, 1.0f, 1.0f,
            0.5f, 0.5f, 0.0f, 1.0f, 0.0f
    };
    // 索引
    GLushort indices[] = {0, 1, 2, 0, 2, 3};

    glViewport(0, 0, esContext->width, esContext->height);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(userData->programObject);
    glVertexAttribPointer(0, 3, GL_FLOAT,
                          GL_FALSE, 5 * sizeof(GLfloat), vertices);
    glVertexAttribPointer(1, 2, GL_FLOAT,
                          GL_FALSE, 5 * sizeof(GLfloat), &vertices[3]);
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, userData->baseMapTexId);
    glUniform1i(userData->baseMapLoc, 0);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, userData->lightMapTexId);
    glUniform1i(userData->lightMapLoc, 1);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容