Можно ли использовать спрайты с точками OpenGL для имитации афишных рекламных щитов?
Я пытался установить точечные спрайты в OpenGL, чтобы изменить размер с расстоянием так же, как и рекламный щит, но я не могу получить значения в GL_POINT_DISTANCE_ATTENUATION_ARB
, чтобы сделать что-нибудь полезное. Существует ли соотношение значений к этому, которое соответствует заданной проекции? Это то, что я пытаюсь сделать даже возможно?
Используемый код Render:
glPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC)wglGetProcAddress("glPointParameterfARB");
glPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC)wglGetProcAddress("glPointParameterfvARB");
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluPerspective(100.0, 800.0/600.0, 0.1, 10.0);
float quadratic[] = { 5.0f, 0.1f, 10.0f };
glPointParameterfvARB( GL_POINT_DISTANCE_ATTENUATION_ARB, quadratic );
float maxSize = 0.0f;
glGetFloatv( GL_POINT_SIZE_MAX_ARB, &maxSize );
if( maxSize > 100.0f ) maxSize = 100.0f;
glPointSize( maxSize );
glPointParameterfARB( GL_POINT_FADE_THRESHOLD_SIZE_ARB, 0.1f );
glPointParameterfARB( GL_POINT_SIZE_MIN_ARB, 0.1f );
glPointParameterfARB( GL_POINT_SIZE_MAX_ARB, maxSize );
glTexEnvf( GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE );
glEnable( GL_POINT_SPRITE_ARB );
glScalef(0.75,1,1);
glTranslatef(0.00,0.0,-1.0);
glScalef(0.5,0.5,0.5);
glRotatef(counter*0.1+0.5,1.0,1.0,0.0);
glBegin( GL_POINTS );
for( int i = 0; i < 100; ++i )
{
glColor4f( i%10*0.1, i/10*0.1, 0.5, 1.0f );
glVertex3f( i%10*0.2-1.0,i/10*0.2-1.0,
((i%10-5)*(i%10-5)+(i/10-5)*(i/10-5))*0.01 );
}
glEnd();
glDisable( GL_POINT_SPRITE_ARB );
Ответы
Ответ 1
Вот как я подхожу к моему бедному человеку, чтобы масштабировать размер точки:
void render() {
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE_ARB);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glEnable(GL_POINT_SPRITE);
glActiveTexture(GL_TEXTURE0);
glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
/* Activate shader program here */
/* Send pointSize to shader program */
glBegin(GL_POINTS);
/* Render points here */
glVertex3f(...);
glEnd(GL_POINTS);
}
Vertex shader:
uniform float pointSize;
void main() {
gl_Position = ftransform();
gl_PointSize = pointSize / gl_Position.w;
}
Вы можете делать все, что хотите, в шейдере фрагментов, но вам придется вычислять цвет, освещение и текстурирование самостоятельно.
Ответ 2
GLSL в сторону, делать то, что вы хотите, довольно просто с ослаблением расстояния. Видя, как прогнозируемый размер вещей уменьшается квадратично с их расстоянием в перспективных проекциях, вам нужно всего лишь использовать квадратичный коэффициент.
Если вы хотите использовать размер точки, который вы вручную устанавливаете на расстоянии, скажем, 150 единиц от глаза, просто используйте 1/(150 ^ 2) в качестве квадратичного фактора (и ноль для постоянных и линейных факторов - - во всяком случае, вы можете использовать некоторое небольшое число, например 0,01 для постоянного коэффициента, чтобы избежать потенциальных делений на ноль).
Ответ 3
По моему опыту, размер затухания не стоит проблем. Вам гораздо лучше писать очень простой вершинный шейдер GLSL, который устанавливает размер точки вручную в соответствии с некоторыми вычислениями, которые вы выполняете самостоятельно. Мне потребовалось около полудня, чтобы узнать с нуля все GLSL, которые мне нужны, чтобы это произошло.
Код GLSL может быть таким же простым, как эти несколько строк:
attribute float psize;
void main()
{
gl_FrontColor = gl_Color;
gl_PointSize = psize;
gl_Position = ftransform();
}
Где psize
- это параметр размера точки, который пользователь выбирает.
Ответ 4
Просто посмотрите в pmviewer.sourceforge.net, код использует точечные спрайты, и каждая точка имеет собственный цвет и размер, чтобы имитировать рендеринг объема:
Вершинный шейдер:
vertexShader
// with ATI hardware, uniform variable MUST be used by output
// variables. That why win_height is used by gl_FrontColor
attribute float a_hsml1;
uniform float win_height;
uniform vec4 cameralocin;
void main()
{
vec4 position=gl_ModelViewMatrix*gl_Vertex;
vec4 cameraloc=gl_ModelViewMatrix*cameralocin;
float d=distance(vec3(cameraloc),vec3(position));
float a_hsml=gl_Normal.x;
float pointSize=win_height*a_hsml/d; // <- point diameter in
//pixels (drops like sqrt(1/r^2))
gl_PointSize=pointSize;
gl_TexCoord[0]=gl_MultiTexCoord0;
gl_Position=ftransform();
gl_FrontColor=vec4(gl_Color.r,gl_Color.g,gl_Color.b,gl_Color.a);
}
Pixelshader
uniform sampler2D splatTexture;
void main()
{
vec4 color = gl_Color * texture2D(splatTexture, gl_TexCoord[0].st);
gl_FragColor = color;\n"
}
Просто отправить частицы в gpu:
void PutOneArrayToGPU(unsigned int m_vbo, float *hArray, unsigned int num)
{
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * num, hArray, GL_STATIC_DRAW);
int size = 0;
glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &size);
if ((unsigned)size != (sizeof(float) *num))
{
fprintf(stderr, "WARNING: Pixel Buffer Object allocation failed!\n");
fprintf(stderr, "TurningOff the GPU accelerated rendering\n");
flag_GpuRender=false;
}
return flag_GpuRender;
}
Затем выполните их:
void DrawPointsByGPU()
{
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, m_vboPos);
glVertexPointer(3, GL_FLOAT, 0, 0);
glEnableClientState(GL_COLOR_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, m_vboColor);
glColorPointer(4, GL_FLOAT, 0, 0);
glEnableClientState(GL_NORMAL_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, m_vboHSML);
glNormalPointer( GL_FLOAT, 3*sizeof(float), 0);
glDrawArrays(GL_POINTS, 0, m_numParticles);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
};