Войти
ПрограммированиеФорумГрафика

Шейдр glsl

Страницы: 1 2 3 Следующая »
#0
4:03, 10 сен. 2011

Делаю встраиваемые шейдры для игрового движка. Уже три месяца не могу понять, в чём секрет сего дзена:

Фраг. шейдр:

uniform int c;void main(){gl_FragColor = vec4(c/255);}

Код в С:

glUseProgram(prog);//частая ошибка - забыл вызвать программу скажете вы. Не тут-то было
glUniform1i(0, (int)time); //time - глобальная float переменная, ведет отсчет времени вроде 0.1, 0.5, 21.42...
//рисуем квадрат
Окей. Этот шейдр работает превосходно. А вот этот не работает:
Фраг. шейдр:
uniform float c;void main(){gl_FragColor = vec4(sin(c));}

Код в С:

glUseProgram(prog);
glUniform1f(0, time);
//рисуем квадрат
в результате получаю мерцающий с бешеной частотой квадрат. А вообще хотел получить мерцающий по синусоиде квадрат. Если установить константу вместо time (0.5, 1, 10) - квад пропадает как будто переменная содержит ноль. Было перепробовано over 9000 вариантов шейдров и везде такой плачевный результат с float'aми. Как ни странно, результат одинаков на разных видеокартах (сначала грешил на глюк драйвера Intel для Linux).

В чём секрет?


#1
9:36, 10 сен. 2011

Ты, должно быть, очень быстро увеличиваешь угол, синус которого вычисляешь. У тебя время в чём измеряется? Если в секундах, тогда всё должно быть нормально.

#2
11:04, 10 сен. 2011

RPG
> glUniform1i(0, (int)time);
  А с чего ты взял, что там должен быть именно ноль? Сначала узнавай индекс переменной. Если узнаешь и будешь использовать его, то хотя бы в этом месте ошибки точно не будет.

> в результате получаю мерцающий с бешеной частотой квадрат.
  Ну попробуй sin(c/100.0) - неужели так трудно догадаться?

  Ещё попробуй вызывать glGetError после инициализации и после каждого кадра и выводи в консоль какую-нибудь инфу при возникновении ошибки.
  Плюс есть ещё функции, которые позволяют узнать лог шейдера. У каждого шейдера и у программы в целом лог свой, их тоже не помешает прочитать.

#3
11:14, 10 сен. 2011

glUniform1i(0, (int)time); - ты передаешь глобальную float переменную как int !!!
Разберись со своим таймером !!!

vec4(sin(c)) будет влиять на прозрачность, наверное надо както так
vec4(vec3(sin(c)),1);

0.1, 0.5, 24  - это очень быстро для синусов, попробуй перемножить тайм на 0.001;

#4
14:21, 10 сен. 2011

> Ты, должно быть, очень быстро увеличиваешь угол, синус которого вычисляешь. У тебя время в чём измеряется? Если в секундах, тогда всё должно быть нормально.
В секундах. Синус от секунды это нормально - полный период 6 секунд с небольшим.
> А с чего ты взял, что там должен быть именно ноль? Сначала узнавай индекс переменной. Если узнаешь и будешь использовать его, то хотя бы в этом месте ошибки точно не будет.
Не думайте что я совсем идиот) Индекс правильный. Инт работает. Да и с индексом я пробовал.
> Ну попробуй sin(c/100.0) - неужели так трудно догадаться?
Результат аналогичен хоть cхоть sin(c) хоть sin(c/100000).
>Ещё попробуй вызывать glGetError после инициализации и после каждого кадра и выводи в консоль какую-нибудь инфу при возникновении ошибки.
Ошибок нет. напомню, что для инта всё ок.
> Плюс есть ещё функции, которые позволяют узнать лог шейдера. У каждого шейдера и у программы в целом лог свой, их тоже не помешает прочитать.
Дельный совет, но я знаю только что шейдр генерит лог в случае ошибок компиляции, а про лог исполнения не слышал. Как его вывести? Но в любом случае попробую логи подхимичить.

> glUniform1i(0, (int)time); - ты передаешь глобальную float переменную как int !!!
> Разберись со своим таймером !!!
Этот код РАБОТАЕТ

> vec4(sin(c)) будет влиять на прозрачность, наверное надо както так
> vec4(vec3(sin(c)),1);
Будет. Это не важно, прозрачность с цветом должна меняться плавно.
> 0.1, 0.5, 24 - это очень быстро для синусов, попробуй перемножить тайм на 0.001;
Краткий курс математики я изложил в начале сообщения. Может быть в шейдрах другой синус? Да ладно, шучу. Я пробовал передавать не time, а константу - 1.0, 0.5 - нифига не передаёт.

#5
15:18, 10 сен. 2011

> попробуй для второго случая :
> glUniform1f(0, time);
чем это отличается от моего второго случая?
> и то что данная переменная будет с индексом 0 всегда тебе никто не гарантирует, так что правильно получать индекс из gl
У меня программа получает индекс и так и так. Принтфом там ноль выходит, так что всё нормально, дело явно не в этом. Более того, если бы я ошибся с юниформом, квадрат бы не мерцал, а просто не был виден.

У меня создается ощущение, что в этом шейдре как-то нарушается структура float и вместо привычных последовательностей там возникает хаос... Ну например так бы было если бы int перевели во флоат без конвертации - просто последовательность байт инта считали последовательностью байт флоата.

Да на самом деле исходник шейдра значительно сложнее - так как движок и игровая логика отделены. Может быть конечно истинный исходный код поможет:
http://ompldr.org/vYWE4Nw
main.lua - прога. Shader.c - код движка.

#6
16:15, 10 сен. 2011

Вот набросал рабочую демку:

local shader = S.newShader('data/shader/simple.vert', 'data/shader/blink.frag')
local butt = S.newImage('data/menu-button.png')
E:new(screen):draw(function(s)
  S.useShader(shader)
  S.setUniformi('time', time)
  butt:draw(0,0,0,800,600)
  S.useShader(0)
end)
blink.frag:
uniform int time;
void main(void)
{
  vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / vec2(800,600);
  vec2 cc = vec2( cos(.25*time), sin(.25*time*1.423) );

  float dmin = 1000.0;
  vec2 z  = p*vec2(1.33,1.0);
  for( int i=0; i<32; i++ )
  {
    z = cc + vec2( z.x*z.x - z.y*z.y, 2.0*z.x*z.y );
    float m2 = dot(z,z);
    if( m2>100.0 ) break;
    dmin=min(dmin,m2);
  }

  float color = sqrt(sqrt(dmin))*0.7;
  gl_FragColor = vec4(color,color,color,1.0);
}
В итоге фрактал обновляется раз в секунду:)
Изображение

#7
17:11, 10 сен. 2011

> float c в радианы переводишь?
не думаю, что в этом проблема:) Делил и на 100 и на 100500.

Если кому интересно наблюдать этот эффект - могу скинуть движок со скриптом и шейдром. Единственное ограничение: движок для Linux и по сему собран под 32bit Linux.

#8
9:33, 11 сен. 2011

Прикрутил твой шейдер к моему движку =)
результат (слева фпс в консоле):

Untitled-1 | Шейдр glsl

код:

#include "ge.h";

#pragma comment (lib,"ge.lib")

class my_scene:public ge_scene{
public:

  ge_model_storage model;
  ge_texture_storage texture;
  ge_mesh_storage mesh;
  mat4 matrix;
  unsigned int t;
  unsigned int timer_location;

  virtual void on_start(){
    timer_location = glGetUniformLocation(ge.render.shader.gl_shader_program,"time");
    glClearColor(0.2f,0.2f,0.2f,1);

    mesh["mesh"].vertex( -1,1,0);
    mesh["mesh"].vertex( 1,1,0);
    mesh["mesh"].vertex( 1,-1,0);

    mesh["mesh"].vertex( -1,1,0);
    mesh["mesh"].vertex( 1,-1,0);
    mesh["mesh"].vertex( -1,-1,0);

    model["model"].mesh_object["mesh"].mesh = &mesh["mesh"];
    model["model"].lock();

    t=ge.timer.ticks+1000;
  }

  virtual void on_draw(){
    if (t<=ge.timer.ticks) {//выводим fps раз в секунду
      printf("fps %d\n",ge.timer.fps);
      t=ge.timer.ticks+1000;
    }

    //!!! AHTUNG !!! WARNING !!! расчет FLOAT таймера и его передача в шейдере !!! WARNING !!!
    float ftime = (float)ge.timer.ticks/1000;
    glUniform1f(timer_location,ftime);
    
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

    matrix.load_identity();
    model["model"].render(matrix,0);

    ge.render.out(GE_NONE);//выводим модель
    ge.render.flip();//выводим картинку на экран
  }

  virtual void on_resize(unsigned int width, unsigned int height){
    ge.render.projection_matrix.load_identity();
  }

} my_scene;


int main(int argc, char **argv){
  if (ge.init(800,600,32,false,L"Fractal")) {
    ge.register_scene("my_scene",&my_scene);
    ge.run("my_scene");
  }
  return 0;
}
#9
10:10, 11 сен. 2011

И возвращаясь к началу топика.
Сделал в шейдере так

color = vec4(col*4,col*2,sin(time),1.0);

Работает отлично, цвета мягко переливаются, никаких рывков.
Так что это не проблема шейдера.

Вот результат:
http://www.youtube.com/watch?v=OTmglWjvOcI

Исходники под 3 OpenGL Windows 7 Если хочешь могу скинуть.

#10
13:44, 11 сен. 2011

Да проблема явно не в шейдре это понятно - замена инт на флот не должна так реагировать...

Больно много фпс в шйдре) на порядок быстрее чем у меня. Кстати если в шейдре выполнить разворот цикла, фпс увеличивается на 20-30%.

У меня только такие мысли:

1. Глючит драйвер для линукс
2. Глючит компилятор
3. Что-то не так из-за того что движок на LUA

P.S. Гугление вопроса привело меня в эту же тему...

Единственное чем могу облегчить задачу - выложить исходник целиком.

char * loadfile(const char * name, unsigned int * length)
{
  char * data;
  PHYSFS_file* myfile = PHYSFS_openRead(name);
  if (!myfile)
    return 0;
  unsigned int len;
  len = PHYSFS_fileLength(myfile);
  *length = len;
  data = (char *)malloc(sizeof(char) * len + 1);
  PHYSFS_read (myfile, data, sizeof(char), len);
  PHYSFS_close(myfile);
  data[len] = 0;
  return data;
}

int compile(GLuint shader, const char* name)
{
  glCompileShader(shader);
  GLint compiled;
  GLint blen = 0;
  GLsizei slen = 0;
  GLchar* compiler_log;
  glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
  if (!compiled)
  {
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH , &blen);       
    if (blen > 1)
    {
      compiler_log = (GLchar*)malloc(blen);
      glGetInfoLogARB(shader, blen, &slen, compiler_log);
      printf("Error while compiling shader %s: %s\n", name, compiler_log);
      free (compiler_log);
    }
    return 0;
  }
  return 1;
}

static int Lua_Shader_load(lua_State *L)
{
  char *vs,*fs;
  const char* vsname = luaL_checkstring(L, 1); //vertex shader
  const char* fsname = luaL_checkstring(L, 2); //fragment shader
  GLuint v, f, p;
  //create shader
  v = glCreateShader(GL_VERTEX_SHADER);
  f = glCreateShader(GL_FRAGMENT_SHADER);
  unsigned int vslen, fslen;
  //loading shader source
  vs = loadfile(vsname, &vslen);
  fs = loadfile(fsname, &fslen);
  glShaderSource(v, 1, &vs, NULL);
  glShaderSource(f, 1, &fs, NULL);
  free(vs);
  free(fs);
  if(!compile(v, vsname))
  {
    lua_pushnil(L);
    return 1;
  }
  if(!compile(f, fsname))
  {
    lua_pushnil(L);
    return 1;
  }
  p = glCreateProgram();
  glAttachShader(p,v);
  glAttachShader(p,f);
  glLinkProgram(p);
  GLint linked;
  glGetProgramiv(p, GL_LINK_STATUS, &linked);
  if (!linked)
  {
    printf("Error while linking shader %s %s\n", vsname, fsname);
    lua_pushnil(L);
    return 1;
  }
  lua_pushinteger(L, p);
  return 1;
}

GLuint current_program;
static int Lua_Shader_use(lua_State *L)
{
  current_program = (GLuint)luaL_checkint(L, 1);
  glUseProgram(current_program);
}

GLuint getUniform(lua_State *L)
{
  if(lua_isnumber(L, 1)) return luaL_checkint(L, 1);
  const char *name;
  GLuint location;
  name = lua_tostring(L, 1);
  location = glGetUniformLocation(current_program, name);
  if(location == -1) return luaL_error(L, "Error: uniform '%s' not found", name);
  return location;
}

static int Lua_Shader_setUniformf(lua_State *L)
{
  switch(lua_gettop(L))
  {
    case 2:
      glUniform1f(getUniform(L), (GLfloat)luaL_checknumber(L, 2));
      break;
  }
  return 0;
}

static const struct luaL_Reg shaderlib [] = {
  {"newShader", Lua_Shader_load},
  {"useShader", Lua_Shader_use},
  {"setUniformf", Lua_Shader_setUniformf},
  {"setUniformi", Lua_Shader_setUniformi},
  {NULL, NULL}
};


int luaopen_shader(lua_State *L, const char *parent) {
  luaL_register(L, parent, shaderlib);
  return 1;
}
#11
14:06, 11 сен. 2011

вот последний код мне не о чем не говорит, ты мне лучше покажи где у тебя вычисляется переменная time

возможно тут после униформа добавь
printf("%f\n",(GLfloat)luaL_checknumber(L, 2)); и посмотри что именно ты посылаешь в шейдер =)

static int Lua_Shader_setUniformf(lua_State *L)
{
  switch(lua_gettop(L))
  {
    case 2:
      glUniform1f(getUniform(L), (GLfloat)luaL_checknumber(L, 2));
      break;
  }
  return 0;
}
#12
15:00, 11 сен. 2011

Хорошая идея. Усложняю:

rpg@rpg$ ./bin/Release/engine 
0.244000
0.256000
0.285000
0.304000
0.318000
0.339000
0.354000
0.367000
0.381000
0.395000
0.414000
0.428000
0.442000
0.456000
0.481000
0.495000
0.509000
0.526000
0.551000
0.569000
0.585000
0.600000
0.616000
0.630000
0.644000
0.663000
0.688000
0.716000
0.730000
0.751000
0.782000
0.796000
0.808000
0.839000
0.852000
0.866000
0.881000
0.896000
0.920000
0.934000
0.948000
0.962000
0.976000
0.990000
1.019000
1.035000
1.050000
1.065000
1.078000
1.093000
1.105000
1.119000
1.136000
1.150000
1.169000
1.196000
1.222000
1.245000
1.259000
1.273000
1.289000
1.302000
1.326000
1.345000
1.373000
1.387000
1.414000
1.439000
1.463000
1.476000
1.491000
1.504000
Просто я не могу понять, что в приведённом выше коде не так...

#13
15:54, 11 сен. 2011

хм ну до glUniform все нормально, после думаю тоже
незнаю даже..., все что могу предложить это явно спросить номер переменной тайм
timer_location = glGetUniformLocation(ge.render.shader.gl_shader_program,"time");
если и это не поможет то дрова и карта

#14
16:06, 11 сен. 2011

Вот этот кусок кода динамически определяет, была передана инт или стринг переменная. Если стринг, вызывается glGetUniformLocation (правда, каждый кадр, что не есть хорошо, но это я могу потом исправить).

GLuint getUniform(lua_State *L)
{
  if(lua_isnumber(L, 1)) return luaL_checkint(L, 1);
  const char *name;
  GLuint location;
  name = lua_tostring(L, 1);
  location = glGetUniformLocation(current_program, name);
  if(location == -1) return luaL_error(L, "Error: uniform '%s' not found", name);
  return location;
}
После gluniform идёт рендеринг... Сомневаюсь, что от того, как именно рендерится квад, зависит переменная time в шейдре.

Тем не менее, на этой карте и на этих дровах нормально идут другие шейдерные движки.

Страницы: 1 2 3 Следующая »
ПрограммированиеФорумГрафика

Тема в архиве.