OpenGL ES应用开发实践指南(android 卷)笔记 第三章1

第三章 编译着色器及在屏幕上绘图

1.理解OpenGL的程序

简单来说,一个OpenGL程序就是把一个顶点着色器和一个片段着色器链接在一起变成单个对象。顶点着色器和片段着色器总是一起工作的。没有片段着色器,OpenGL就不知道怎么绘制那些组成每个点、直线和三角形的片段;如果没有顶点着色器,OpenGL就不知道在哪里绘制这些片段。

我们知道顶点着色器计算屏幕上每个顶点的最终位置。我们也知道OpenGL把这些顶点组织成点、直线和三角形并且分解成片段的时候,它就会向片段着色器询问每个片段最后的颜色。顶点和片段着色器一起合作生成屏幕上最终的图像。虽然顶点着色器和片段着色器总是要一起工作,但并不意味着它们必须是一对一匹配的,我们可以同事在多个程序中使用同一个着色器。


public class TextResourceReader {

    public static String readTextFileFromResource(Context context, int resourceId){

        StringBuilder builder = new StringBuilder();
        try {

            InputStream inputStream = context.getResources().openRawResource(resourceId);
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String nextLine = "";
            while ((nextLine = bufferedReader.readLine()) != null){
                builder.append(nextLine);
                builder.append('\n');
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("Could not open resource:"+resourceId, e);
        } catch (Resources.NotFoundException nfc){
            throw new RuntimeException("Resource not found:"+resourceId);
        }

        return builder.toString();
    }

}

public class ShaderHelper {
    private static final String TAG = "ShaderHelper";

    public static int compileVertexShader(String shaderCode){
        return compileShader(GL_VERTEX_SHADER,shaderCode);
    }

    public static int compileFragmentShader(String shaderCode){
        return compileShader(GL_FRAGMENT_SHADER,shaderCode);
    }

    /**
     * 这里用glCreateShader()调用创建了一个新的着色器对象,并把这个对象的ID存入变量shaderObjectId。
     * 这个type可以是代表顶点着色器的GL_VERTEX_SHADER,或者是代表片段着色器的GL_FRAGMENT_SHADER.
     * 剩下的代码也用同样的方式。
     * shaderObjectId是OpenGL对象的引用。无论后面什么时候想要引用这个对象,就要把这个整型值传回OpenGL
     * 返回值0表示这个对象创建失败,它类似于Java代码中返回null值。
     * 如果对象创建失败,就给调用代码返回0.为什么返回0而不是抛出一个异常呢?
     * 这是因为OpenGL内部实际不会抛出任何异常;相反,我们会得到返回值0,并且OpenGL通过glGetError()告诉我们这个错误
     * 这个方法可以让我们询问OpenGL是不是某个API调用导致了错误。我们会一直遵从这个惯例。
     *
     * @param type
     * @param shaderCode
     * @return
     */
    private static int compileShader(int type, String shaderCode){
        final int shaderObjectId = glCreateShader(type);
        if(shaderObjectId == 0){
            if(LoggerConfig.ON){
                Log.w(TAG,"Could not create new shader.");
            }
            return 0;
        }

        //编译先前上传到ShaderObjectId的源代码
        glCompileShader(shaderObjectId);

        /**
         * 为了检查编译是失败还是成功,首先要创建一个新的长度为1的int数组,称为compileStatue;然后调用glGetShaderiv()。
         * 这就告诉OpenGL读取shaderObjectId关联的编译状态,并把它写入compileStatus的第0个元素。
         * 这是Android平台上的OpenGL的另外一个通用模式。为了取出一个值,我们通常会使用一个长度为1的数组,并把这个数组传进一个OpenGl调用。
         * 在同一个调用中,我们告诉OpenGL把结果存进的第一个元素中。
         */
        final int[] compileStatues = new int[1];
        glGetShaderiv(shaderObjectId,GL_COMPILE_STATUS,compileStatues,0);

        if(LoggerConfig.ON){
            Log.v(TAG, "Results of compiling source:"+"\n"+shaderCode+"\n:"+glGetShaderInfoLog(shaderObjectId));
        }

        /**
         * 验证编译状态并返回着色器对象
         */

        if(compileStatues[0] == 0){
            glDeleteShader(shaderObjectId);

            if(LoggerConfig.ON){
                Log.w(TAG, "Compilation of shader failed");
            }

            return 0;
        }


        return shaderObjectId;
    }

    /**
     * 这个语意同我们早前创建一个新的着色器对象是一样的:返回的整型值是程序对象的引用,如果对象创建失败,我们就会得到返回值0
     * @param vertexShaderId
     * @param fragmentShaderId
     * @return
     */
    public static int linkProgram(int vertexShaderId, int fragmentShaderId){
        final int programeObjectId = glCreateProgram();

        if(programeObjectId == 0){
            if(LoggerConfig.ON){
                Log.w(TAG, "Could not create new Program");
            }
            return 0;
        }

        //附上着色器
        glAttachShader(programeObjectId, vertexShaderId);
        glAttachShader(programeObjectId,fragmentShaderId);

        //现在准备把这些着色器联合起来了,为此,将调用glLinkProgram()
        glLinkProgram(programeObjectId);

        //为了检查这个链接是成功还是失败,我们会遵循编译着色器时所使用的步骤
        final int[] linkStatues = new int[1];
        glGetProgramiv(programeObjectId,GL_LINK_STATUS,linkStatues,0);
        if(LoggerConfig.ON){
            Log.v(TAG, "Results of linking program:\n"+glGetProgramInfoLog(programeObjectId));
        }

        /**
         * 验证链接状态并返回程序对象ID
         * 现在需要检查链接状态:如果它是0,就意味着这个链接失败,这个程序对象将无法被使用,因此要把它删除并返回0给调用代码
         */
        if(linkStatues[0] == 0){
            glDeleteProgram(programeObjectId);
            if(LoggerConfig.ON){
                Log.w(TAG, "linking of program failed");
            }
            return 0;
        }

        return programeObjectId;
    }

    /**
     * 开始使用OpenGL的程序之前,我们首先应该验证一下它,看看这个程序对于当前的OpenGl状态是不是有效的。根据OpenGL ES 2.0的文档,
     * 它也给OpenGL提供了一种方法让我们知道为什么当前的程序可能是低效率的、无法运行,等等。
     * @param programObjectId
     * @return
     */
    public static boolean validateProgram(int programObjectId){
        glValidateProgram(programObjectId);

        final int[] validateStatus = new int[1];
        glGetProgramiv(programObjectId,GL_VALIDATE_STATUS,validateStatus,0);
        Log.v(TAG,"Results of validating program:"+validateStatus[0] + "\nLog:"+glGetProgramInfoLog(programObjectId));

        return validateStatus[0] != 0;
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值