Делаем спрайт разных цветов

от автора

Я пробую делать игры. Пишу код, пробую рисовать и хочу научиться делать музыку. Я прорисовал персонажа и мне нужно было сделать так, чтобы ему можно было менять цвет одежды. Решение пришло сначала одно, но потом додумался до более простого.

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

как можно заметить вся одежка в оттенках серого цвета. Это оказалось удобным. я выписал числовые коды цветов в отдельный файл.

human colors ------------------------------------------- body front #6a6a6a 0.41 0.41 0.41 body up #959595 0.58 0.58 0.58 arm #a6a6a6  0.65 0.65 0.65 arm blick  #c1c1c1  0.75 0.75 0.75 arm blacked #8a8a8a  0.23 body down #5b5b5b 0.35 0.35 0.35 left legs blacked #4c4c4c 0.29 0.29 0.29 left legs blick #5c5c5c 0.36 0.36 0.36 right legs blacked #656565 0.39 0.39 0.39 right legs blick #808080 0.50 0.50 0.50

вот код конструктора.

Sprite::Sprite (int x, int y, int z, const char *path) {     this->visibility = true;     this->isAnimate = false;     this->animator = new Animator ();     this->check = 0;     this->commonData = nullptr;     this->frame = nullptr;     this->trigger_shadow = 0;          this->z = glm::vec2 ((float) z, 1.0f);          GameObject::ortho = glm::ortho (0.0f, 1.0f, 1.0f, 0.0f, -100.0f, 100.0f);          float xx = ((float) x) / ((float) mea_width);     float yy = ((float) y) / ((float) mea_height);     float zz = z;     GameObject::pos = glm::translate (glm::mat4(1.0f), glm::vec3 (xx, yy, zz));      GameObject::model = glm::mat4(1.0f);      this->shader = shaderController->program[SHADERS::SPRITE];     this->type_shader = SHADERS::SPRITE;      gl::UseProgram (shaderController->program[SHADERS::SPRITE]);     this->location_view = gl::GetUniformLocation (shaderController->program[SHADERS::SPRITE], "view");     this->location_tex = gl::GetUniformLocation (shaderController->program[SHADERS::SPRITE], "tex");     this->location_pos = gl::GetUniformLocation (shaderController->program[SHADERS::SPRITE], "pos");     this->location_model = gl::GetUniformLocation (shaderController->program[SHADERS::SPRITE], "model");     this->location_scale = gl::GetUniformLocation (shaderController->program[SHADERS::SPRITE], "scale");     this->location_z = gl::GetUniformLocation (shaderController->program[SHADERS::SPRITE], "zCoord");            if (path != nullptr) loadSprite (path); }

Как я помню, в играх указывают что например 1920×1080 разрешение рекомендуемое и я додумался сделать так — создать переменные mea_width = 1920 и mea_height = 1080. Ortho я выставляю в 1.0f расстояние, но чтобы одинаково везде было, я высчитываю деление на mea_*. То есть для тестирования и удобной работы я использую full hd экран и мне удобней ориентироваться в этих координатах. В игре я также оперирую координатами 1920×1080, но они преобразуются в 1.0f. Это делается очень просто, вот так.

float xx = ((float) x) / ((float) mea_width); float yy = ((float) y) / ((float) mea_height);

Потом есть загрузка шейдера в спрайт, для спрайта мы просто указываем тип шейдера.

void Sprite::setShader (int shader) {     this->type_shader = shader;      switch (shader) {         case SHADERS::SPRITE:             this->shader = shaderController->program[SHADERS::SPRITE];             shader = this->shader;             gl::UseProgram (shader);             this->location_view = gl::GetUniformLocation (shader, "view");             this->location_tex = gl::GetUniformLocation (shader, "tex");             this->location_pos = gl::GetUniformLocation (shader, "pos");             this->location_model = gl::GetUniformLocation (shader, "model");             this->location_scale = gl::GetUniformLocation (shader, "scale");             this->location_z = gl::GetUniformLocation (shader, "zCoord");         break;         case SHADERS::SPRITE_WITH_ALPHA_COLORS:             this->shader = shaderController->program[SHADERS::SPRITE_WITH_ALPHA_COLORS];             shader = this->shader;             gl::UseProgram (shader);             this->location_view = gl::GetUniformLocation (shader, "view");             this->location_tex = gl::GetUniformLocation (shader, "tex");             this->location_pos = gl::GetUniformLocation (shader, "pos");             this->location_model = gl::GetUniformLocation (shader, "model");             this->location_scale = gl::GetUniformLocation (shader, "scale");             this->location_z = gl::GetUniformLocation (shader, "zCoord");             this->location_one = gl::GetUniformLocation (shader, "color_wall");             this->location_two = gl::GetUniformLocation (shader, "color_floor");         break;         case SHADERS::SPRITE_HUMAN:             this->shader = shaderController->program[SHADERS::SPRITE_HUMAN];             shader = this->shader;             gl::UseProgram (shader);             this->location_view = gl::GetUniformLocation (shader, "view");             this->location_tex = gl::GetUniformLocation (shader, "tex");             this->location_pos = gl::GetUniformLocation (shader, "pos");             this->location_model = gl::GetUniformLocation (shader, "model");             this->location_scale = gl::GetUniformLocation (shader, "scale");             this->location_z = gl::GetUniformLocation (shader, "zCoord");             this->location_one = gl::GetUniformLocation (shader, "color_up");             this->location_two = gl::GetUniformLocation (shader, "color_down");         break;     }      }

Как и объяснялось ранее, я координаты указываю within 1920×1080.

void Sprite::setPos (int x, int y, int z) {     float xx = ((float) x) / ((float) mea_width);     float yy = ((float) y) / ((float) mea_height);     float zz = z;     GameObject::pos = glm::translate (glm::mat4(1.0f), glm::vec3 (xx, yy, zz));     this->x = x;     this->y = y; }

тот же расчет я использую и для задания размера спрайта, но код наверное не буду здесь приводить.

отрисовка происходит по типу спрайта и вот пример отрисовки с нужным кадром.

void Sprite::draw_frame_with_color (int frame) {      gl::UseProgram (this->shader);      gl::Uniform1i (this->location_tex, 0);      gl::ActiveTexture (gl::TEXTURE0);      gl::BindTexture (gl::TEXTURE_2D, this->frame[frame]);         gl::Uniform3f (this->location_one, this->color_one.r, this->color_one.g, this->color_one.b);     gl::Uniform3f (this->location_two, this->color_two.r, this->color_two.g, this->color_two.b);      gl::UniformMatrix4fv (this->location_view, 1, gl::FALSE_, &GameObject::ortho[0][0]);      gl::UniformMatrix4fv (this->location_pos, 1, gl::FALSE_, &GameObject::pos[0][0]);      gl::UniformMatrix4fv (this->location_model, 1, gl::FALSE_, &GameObject::model[0][0]);     gl::UniformMatrix4fv (this->location_scale, 1, gl::FALSE_, &GameObject::scale[0][0]);      gl::Uniform2f (this->location_z, this->z[0], this->z[1]);      gl::BindVertexArray (this->vao);      gl::EnableVertexAttribArray (0);     gl::EnableVertexAttribArray (1);      gl::DrawArrays (gl::TRIANGLES, 0, 6);    }

Как видно из кода, я передаю в шейдер две uniform переменные, color_one и color_two. Они как раз и служат целью задания цвета для одежды персонажа. Шейдер фрагментный получился такой.

#version 330 core  out vec4 fragColor;  in vec2 texCoord;  uniform sampler2D tex;  uniform vec3 color_up; uniform vec3 color_down;  void main () {     vec3 color = texture (tex, texCoord).rgb;     float r = color.r;     float g = color.g;     float b = color.b;     r *= 100;     g *= 100;     b *= 100;     int rr = int(r);     int gg = int(g);     int bb = int(b);     r = rr;     g = gg;     b = bb;     r *= 0.01;     g *= 0.01;     b *= 0.01;     color = vec3 (r, g, b);      if (color == vec3 (0.41, 0.41, 0.41)) {         fragColor = vec4 (color_up, 1.0);     } else if (color == vec3 (0.58, 0.58, 0.58)) {         vec3 col = color_up + vec3 (0.17, 0.17, 0.17);         fragColor = vec4 (col, 1.0);     } else if (color == vec3 (0.65, 0.65, 0.65)) {         vec3 col = color_up + vec3 (0.24, 0.24, 0.24);         fragColor = vec4 (col, 1.0);     } else if (color == vec3 (0.75, 0.75, 0.75)) {         vec3 col = color_up + vec3 (0.34, 0.34, 0.34);         fragColor = vec4 (col, 1.0);     } else if (color == vec3 (0.23, 0.23, 0.23)) {         vec3 col = color_up - vec3 (0.18, 0.18, 0.18);         fragColor = vec4 (col, 1.0);     } else if (color == vec3 (0.35, 0.35, 0.35)) {         fragColor = vec4 (color_down, 1.0);     } else if (color == vec3 (0.29, 0.29, 0.29)) {         vec3 col = color_down - vec3 (0.06, 0.06, 0.06);         fragColor = vec4 (col, 1.0);     } else if (color == vec3 (0.36, 0.36, 0.36)) {         vec3 col = color_down + vec3 (0.01, 0.01, 0.01);         fragColor = vec4 (col, 1.0);     } else if (color == vec3 (0.39, 0.39, 0.39)) {         vec3 col = color_down + vec3 (0.04, 0.04, 0.04);         fragColor = vec4 (col, 1.0);     } else if (color == vec3 (0.50, 0.50, 0.50)) {         vec3 col = color_down + vec3 (0.15, 0.15, 0.15);         fragColor = vec4 (col, 1.0);     } else {         fragColor = texture (tex, texCoord);     } }

Как видно из кода, он просто проверяет на оттенок цвета и плюсует или минусует оттенок, чтобы добиться правильного цвета. Я такой же шейдер подключил и для столов и теперь когда персонаж подходит к столу и исчезает, он передает свои цвета столу и стол по этим оттенкам уже указывает нужный цвет. И начинает казаться, что персонаж действительно тот, который подходил к столу.

ссылка на оригинал статьи https://habr.com/ru/post/566468/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *