假设我使用自定义渲染器在GLSurfaceView上播放视频,在所述渲染器中,我使用了一个片段着色器,该着色器采用额外的纹理进行查找过滤。所述片段着色器看起来如下:
#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES u_Texture;
uniform sampler2D inputImageTexture2;
varying highp vec2 v_TexCoordinate;
void main()
{
vec3 texel = texture2D(u_Texture, v_TexCoordinate).rgb;
texel = vec3(
texture2D(inputImageTexture2, vec2(texel.r, .16666)).r,
texture2D(inputImageTexture2, vec2(texel.g, .5)).g,
texture2D(inputImageTexture2, vec2(texel.b, .83333)).b
);
gl_FragColor = vec4(texel, 1.0);
}在glUseProgram()调用之后的onDrawFrame()函数中,我有一个onPreDrawFrame()函数,它基本上将纹理绑定到着色器的制服中。它目前看起来是这样的:
public void onPreDrawFrame()
{
if (filterSourceTexture2 != -1 && filterInputTextureUniform2 != -1) {
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, filterSourceTexture2);
GLES20.glUniform1i(filterInputTextureUniform2, 2);
}
}filterSourceTexture2是与额外纹理对应的纹理单位。
让我感到困惑的是,如果我将glUniform1i()调用放在glActiveTexture()之前,它仍然可以很好地工作,但我见过的大多数教程都将glUniform1i()调用放在上面的代码中。
那么哪个是推荐的呢?
发布于 2017-01-19 17:36:08
这两个没有区别。通过均匀着色器传递的是应在片段着色器中使用的活动纹理。接下来,您需要将纹理id绑定到实际的活动纹理。因此,您将在此处看到的唯一顺序是,必须在绑定之前调用活动纹理。
现在,在大多数情况下,您将看到与前面提到的相同的序列:活动、绑定、设置一致。但是,当涉及到优化代码时,这种情况将会改变:
由于您可能想要减少到GPU的流量,因此您将希望减少对制服的冗余调用。一个统一的呼叫不会对你有多大好处,但仍然..。您将初始化着色器,然后设置所有默认制服,这样就不会在每个绘制帧调用时都设置它们。在你的例子中,这意味着你将有两个纹理,对于第一个纹理,这将始终是活动纹理0,对于第二个纹理,将始终是活动纹理1。因此,首先设置两个制服,然后当你需要绑定新纹理时,只需这样做,最好不要在每次绘制调用时都这样做。
其他一些实现包括具有一个自定义着色器对象,该对象会记住以前的设置,因此不会再次向着色器发送相同的值:
if(this.myUniform != newValue) {
this.myUniform = newValue;
GLES20.glUniform1i(filterInputTextureUniform2, newValue);
}但问题是,这个系统最终可能会比你实际上只是重新设置制服的速度更慢。
因此,推荐的实际答案是:如果可能,我建议在某些初始化中首先设置一致,这意味着序列甚至不在相同的方法中。但是如果它们是用相同的方法,我建议你最后设置制服,因为可读性,但这将意味着首先设置两个纹理(活动和绑定),然后依次设置两个制服。
和一个关于“大多数教程”的注释:
您可以找到的大多数教程都旨在向您展示API的工作原理。它们的设计目的是让您能够轻松识别必须进行的调用以及调用的顺序(如果有的话)。这导致通常将所有代码放在单个源文件中,这在实际应用程序中是不应该做的。记住按照您认为合适的方式构建您的工具,并将它们划分到不同的类中。最后,您的"on draw“方法不应超过50行,无论您正在绘制的项目或场景的大小如何。
https://stackoverflow.com/questions/41735381
复制相似问题