片段着色器

OpenGLES可编程管线流程:


片段着色器_第1张图片
可编程管线流程

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

片段着色器_第2张图片
RGB组合函数

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

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


片段着色器_第3张图片
片段着色器输入输出

内建特殊变量:
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);
}

你可能感兴趣的:(片段着色器)