我在为项目编写片段着色器时遇到了一些问题。我正在创建一个无调色板的终端模拟器,所以我想我会使用以下着色器执行此操作:
#version 110
uniform sampler2D tileset;
uniform sampler2D indices;
uniform sampler2D colors;
uniform sampler2D bgcolors;
uniform vec2 tileset_size;
uniform vec2 size;
varying vec2 tex_coord;
void main(void)
{
// Calculated texture coordinate
vec2 screen_pos = vec2(gl_FragCoord.x / 800.0, 1.0 - gl_FragCoord.y / 500.0);
// Indirect texture lookup 1
vec2 index = texture2D(indices, screen_pos.st).rg;
vec4 color = texture2D(colors, screen_pos.st);
vec4 bgcolor = texture2D(bgcolors, screen_pos.st);
// Calculated texture coordinate
vec2 tileCoord;
//256.0 because the [0,256) byte value is normalized on [0,1)
tileCoord.x = mod(screen_pos.x, 1.0/size.x)*(size.x/tileset_size.x) + floor(index.x*256.0)/tileset_size.x;
tileCoord.y = mod(screen_pos.y, 1.0/size.y)*(size.y/tileset_size.y) + floor(index.y*256.0)/tileset_size.y;
// Indirect texture lookup 2
vec4 tile = texture2D(tileset, tileCoord);
vec4 final = tile*color;
gl_FragColor = vec4(mix(bgcolor.rgb, final.rgb, final.a), 1.0);
}
为了将其渲染到屏幕上,我绘制了一个大四边形并让着色器完成剩下的工作。
此代码生成所需的输出。但是,它在每帧的5 秒处执行此操作。根据我的研究,这可能是由于显示驱动程序在软件中执行我的着色器,而不是硬件。我发现通过取消注释texture2D()
来电,事情再次顺利进行。
这使我得到以下代码:
void main(void)
{
//vec2 screen_pos = vec2(gl_FragCoord.x / 800.0, 1.0 - gl_FragCoord.y / 500.0);
vec2 screen_pos = vec2(0.5, 0.5);
vec2 index = texture2D(indices, screen_pos.st).rg;
vec4 color = texture2D(colors, screen_pos.st);
vec4 bgcolor = texture2D(bgcolors, screen_pos.st);
vec4 tiles = texture2D(tileset, screen_pos.st);
gl_FragColor = vec4(index.rgg + color.rgb + bgcolor.rgb + tiles.rgb, 1.0);
}
事实证明这一点非常缓慢。注释掉最后一行vec4 tiles = ...
,并从输出中删除它再次顺利运行。所以我查看了设备支持的texture2D调用次数。我得到了以下结果:
GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB: 8
GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB: 16
GL_MAX_TEXTURE_IMAGE_UNITS_ARB: 8
GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB: 8
所以必须要有所作为。即使我的每个调用都是间接访问(我很确定它们不是),我应该有多达8个!此外,glGetShaderInfoLog()
和glGetProgramInfoLog()
无话可说。
我应该列出我的规格:
是的,我在调用标准glewInit()
程序后检查了GL_ARB_fragment_program。
所以,我有两种可能的解决方案。
如果有人对这方面有任何不可思议的知识,我真的很想听。通常情况下,我会说“搞砸它,买一台更好的电脑”,但是为了运行一个终端模拟器而拥有一个高端显卡的纯粹讽刺是......好吧......具有讽刺意味。
如果我忘了在这里写一些信息,请告诉我。
编辑
glxinfo -l:pastebin
ARB assembly(部分由cgc生成)
禁用任何TEX指令会使其进入硬件模式,所有4个指令都将返回软件。
答案 0 :(得分:1)
好吧,看起来像下面的ARB片段程序组件就可以了。由cgc
生成,但绝大多数都被废弃并手工编写。
!!ARBfp1.0
# cgc version 3.1.0013, build date Apr 18 2012
# command line args: -oglsl -profile arbfp1
# source file: tilemap.frag
#vendor NVIDIA Corporation
#version 3.1.0.13
#profile arbfp1
#program main
#semantic tileset
#semantic indices
#semantic colors
#semantic bgcolors
#semantic tileset_size
#semantic size
#var float4 gl_FragCoord : $vin.WPOS : WPOS : -1 : 1
#var float4 gl_FragColor : $vout.COLOR : COL : -1 : 1
#var sampler2D tileset : : texunit 3 : -1 : 1
#var sampler2D indices : : texunit 0 : -1 : 1
#var sampler2D colors : : texunit 1 : -1 : 1
#var sampler2D bgcolors : : texunit 2 : -1 : 1
#var float2 tileset_size : : c[0] : -1 : 1
#var float2 size : : c[1] : -1 : 1
#var float2 tex_coord : : : -1 : 0
#const c[2] = 0.0020000001 1 0.00125 256
PARAM c[3] = {
program.local[0..1],
{ 0.0020000001, 1, 0.00125, 256 }
};
TEMP R0;
TEMP R1;
TEMP R2;
TEMP R3;
# R2 := normalized screen coords
MAD R2.z, -fragment.position.y, c[2].x, c[2].y;
MUL R2.x, fragment.position, c[2].z;
MOV R2.y, R2.z;
TEX R3, R2, texture[2], 2D;
TEX R0, R2, texture[1], 2D;
TEX R1, R2, texture[0], 2D;
# multiply by screen size
MUL R2.x, R2.x, c[0].x;
MUL R2.y, R2.y, c[0].y;
# backup original
MOV R2.z, R2.x;
MOV R2.w, R2.y;
# multiply by inverse of font size
MUL R2.x, R2.x, c[1].z;
MUL R2.y, R2.y, c[1].w;
FLR R2.x, R2.x;
FLR R2.y, R2.y;
MUL R2.x, R2.x, c[1].x;
MUL R2.y, R2.y, c[1].y;
# now we have a bit of a staircase, take the original minus staircase
ADD R2.x, R2.z, -R2.x;
ADD R2.y, R2.w, -R2.y;
# modulo is complete
# normalize per unit (inv font size)
MUL R2.x, R2.x, c[1].z;
MUL R2.y, R2.y, c[1].w;
# divide by 16 for proper texture offset
MUL R2.x, R2.x, .0625;
MUL R2.y, R2.y, .0625;
# add to given texture offset
ADD R2.x, R2.x, R1.x;
ADD R2.y, R2.y, R1.y;
# ... and sample!
TEX R2, R2, texture[3], 2D;
#R2 is tile color
#R3 is background color
#R0 is color color
MUL R0, R0, R2;
#R0 is result color
SUB R3, R3, R0;
#R3 is bgcolor - rescolor
# lerp R3 (multiply by 1 - r)
MAD R3, R3, -R0.a, R3;
#R3 is (bgcolor - rescolor) * rescolor.a - (bgcolor - rescolor)
ADD result.color, R3, R0;
END
无论出于何种原因,为简化案例写出装配,例如
TEX ...
TEX ...
TEX ...
TEX ...
将着色器置于软件模式,就像之前一样。使用cgc
编译几个不同的版本后,我发现有些版本仍然可以使用4个纹理访问。另外,我交换了最初的内容:
TEX R1, R2, texture[2], 2D;
TEX R0, R2, texture[1], 2D;
ADD R0, R0, R1
TEX R1, R2, texture[0], 2D;
到
TEX R3, R2, texture[2], 2D;
TEX R0, R2, texture[1], 2D;
TEX R1, R2, texture[0], 2D;
# ... addition done later
基于我在the ARB_fragment_program spec
中读到的内容纹理间接可以被视为纹理中的节点 依赖链。每个节点都包含一组纹理 并行执行的指令,后跟一系列的指令 ALU说明。依赖纹理指令就是这样 使用临时作为输入坐标而不是属性 或参数。没有依赖纹理指令的程序 (或根本没有纹理指令)将有一个节点 它的纹理依赖链,因而是单个间接。
所以,至少我删除了一个纹理间接。看来cgc
版本(可能是glsl
编译器)试图最小化 temporaries ,而不是纹理访问。毕竟最终可以使用4个临时工具;我仍然不确定为什么需要这种优化。
此API很难获得文档。我认为2002年是新的?无论哪种方式,我都做到了。
if(!GLEW_ARB_fragment_program)
{
printf("GLEW_ARB_fragment_program is unavailable.\n");
return false;
}
glClear(GL_COLOR_BUFFER_BIT);
SDL_GL_SwapWindow(window);
glEnable(GL_FRAGMENT_PROGRAM_ARB);
glGenProgramsARB(1, &tilemap_prog);
if(!tilemap_prog)
{
printf("Failed to generate fragment program\n");
return false;
}
glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, tilemap_prog);
glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(tilemap_frag_asm), tilemap_frag_asm);
GLuint error = glGetError();
if(error == GL_INVALID_OPERATION)
{
printf("GL_INVALID_OPERATION!\n");
printf("glGetString(GL_PROGRAM_ERROR_STRING_ARB): %s\n", glGetString(GL_PROGRAM_ERROR_STRING_ARB));
GLint texture_units;
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB, &texture_units);
printf("GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB: %d\n", texture_units);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB, &texture_units);
printf("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB: %d\n", texture_units);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &texture_units);
printf("GL_MAX_TEXTURE_IMAGE_UNITS_ARB: %d\n", texture_units);
glGetIntegerv(GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB, &texture_units);
printf("GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB: %d\n", texture_units);
return false;
}
// Window size
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, width, height, 00.0, 00.0);
// Font output size and inverse font output size
glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, 10.0, 10.0, 1/10.0, 1/10.0);
有点挑剔,但它最终奏效了。特别感谢keltar指出我正确的方向。