Class: Bestguigui::Render3D::Shader

Inherits:
Object
  • Object
show all
Includes:
OpenGL
Defined in:
lib/bestguigui/render_3d/shader.rb

Overview

Programme shader GLSL (vertex + fragment) compilé et linké. ObjModel et Billboard utilisent un shader par défaut fourni par la gem (Shader.default) — n'en écris un toi-même que si tu veux un rendu différent (éclairage, effet...).

Constant Summary collapse

DEFAULT_VERTEX =
<<~GLSL
  #version 330 core
  layout(location = 0) in vec3 a_position;
  layout(location = 1) in vec2 a_texcoord;

  uniform mat4 u_model;
  uniform mat4 u_view;
  uniform mat4 u_projection;

  out vec2 v_texcoord;

  void main() {
    gl_Position = u_projection * u_view * u_model * vec4(a_position, 1.0);
    v_texcoord = a_texcoord;
  }
GLSL
DEFAULT_FRAGMENT =
<<~GLSL
  #version 330 core
  in vec2 v_texcoord;
  out vec4 frag_color;

  uniform sampler2D u_texture;
  uniform bool u_alpha_test;
  uniform vec3 u_tint;

  void main() {
    vec4 tex_color = texture(u_texture, v_texcoord);
    if (u_alpha_test && tex_color.a <= 0.0) discard;
    frag_color = vec4(tex_color.rgb * u_tint, tex_color.a);
  }
GLSL
GOURAUD_VERTEX =
<<~GLSL
  #version 330 core
  layout(location = 0) in vec3 a_position;
  layout(location = 1) in vec2 a_texcoord;
  layout(location = 2) in vec3 a_normal;

  uniform mat4 u_model;
  uniform mat4 u_view;
  uniform mat4 u_projection;
  uniform vec3 u_light_dir;   // vers la lumière, normalisé
  uniform vec3 u_light_color;
  uniform float u_ambient;

  out vec2 v_texcoord;
  out vec3 v_light;

  void main() {
    gl_Position = u_projection * u_view * u_model * vec4(a_position, 1.0);
    v_texcoord = a_texcoord;

    // Lighting computed per-vertex here (not per-pixel in the
    // fragment shader), then interpolated across the face - real
    // Gouraud shading, PS1/FF7-style. Ignores non-uniform model
    // scale (raw mat3(u_model)) - good enough for a jam.
    vec3 n = normalize(mat3(u_model) * a_normal);
    // u_light_dir = direction vers laquelle la lumière voyage (ex: un
    // soleil au-dessus pointe vers le bas) -> on l'inverse ici pour
    // avoir la direction vers la source, celle qu'attend Lambert.
    float diffuse = max(dot(n, -u_light_dir), 0.0);
    v_light = u_light_color * (u_ambient + (1.0 - u_ambient) * diffuse);
  }
GLSL
GOURAUD_FRAGMENT =
<<~GLSL
  #version 330 core
  in vec2 v_texcoord;
  in vec3 v_light;
  out vec4 frag_color;

  uniform sampler2D u_texture;
  uniform bool u_alpha_test;
  uniform vec3 u_tint;

  void main() {
    vec4 tex_color = texture(u_texture, v_texcoord);
    if (u_alpha_test && tex_color.a <= 0.0) discard;
    frag_color = vec4(tex_color.rgb * v_light * u_tint, tex_color.a);
  }
GLSL

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(vertex_src, fragment_src) ⇒ Shader

Returns a new instance of Shader.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/bestguigui/render_3d/shader.rb', line 10

def initialize(vertex_src, fragment_src)
  vs = compile(GL_VERTEX_SHADER, vertex_src)
  fs = compile(GL_FRAGMENT_SHADER, fragment_src)

  @program = glCreateProgram()
  glAttachShader(@program, vs)
  glAttachShader(@program, fs)
  glLinkProgram(@program)

  status = status_buffer
  glGetProgramiv(@program, GL_LINK_STATUS, status)
  if status.unpack1("L").zero?
    log = " " * 2048
    glGetProgramInfoLog(@program, 2048, nil, log)
    raise "Bestguigui::Render3D::Shader link error: #{log.unpack1('Z*')}"
  end
end

Class Method Details

.defaultObject

Shader unlit texturé + alpha-test optionnel, utilisé par défaut par ObjModel et Billboard. Une seule instance partagée.



91
92
93
# File 'lib/bestguigui/render_3d/shader.rb', line 91

def self.default
  @default ||= new(DEFAULT_VERTEX, DEFAULT_FRAGMENT)
end

.gouraudObject

Shader texturé + éclairage Gouraud (une lumière directionnelle, voir Bestguigui::Render3D::Light), utilisé par ObjModel.new(..., lighting: true). A besoin de normales par sommet dans le Mesh (layout: :pos_uv_normal) — sans ça, comportement indéfini côté attribut a_normal.



150
151
152
# File 'lib/bestguigui/render_3d/shader.rb', line 150

def self.gouraud
  @gouraud ||= new(GOURAUD_VERTEX, GOURAUD_FRAGMENT)
end

Instance Method Details

#set_bool(name, value) ⇒ Object



42
43
44
# File 'lib/bestguigui/render_3d/shader.rb', line 42

def set_bool(name, value)
  set_int(name, value ? 1 : 0)
end

#set_float(name, value) ⇒ Object



46
47
48
49
# File 'lib/bestguigui/render_3d/shader.rb', line 46

def set_float(name, value)
  loc = glGetUniformLocation(@program, name)
  glUniform1f(loc, value)
end

#set_int(name, value) ⇒ Object



37
38
39
40
# File 'lib/bestguigui/render_3d/shader.rb', line 37

def set_int(name, value)
  loc = glGetUniformLocation(@program, name)
  glUniform1i(loc, value)
end

#set_mat4(name, mat4) ⇒ Object



32
33
34
35
# File 'lib/bestguigui/render_3d/shader.rb', line 32

def set_mat4(name, mat4)
  loc = glGetUniformLocation(@program, name)
  glUniformMatrix4fv(loc, 1, GL_FALSE, mat4.to_a.pack("F16"))
end

#set_vec3(name, r, g, b) ⇒ Object



51
52
53
54
# File 'lib/bestguigui/render_3d/shader.rb', line 51

def set_vec3(name, r, g, b)
  loc = glGetUniformLocation(@program, name)
  glUniform3f(loc, r, g, b)
end

#useObject



28
29
30
# File 'lib/bestguigui/render_3d/shader.rb', line 28

def use
  glUseProgram(@program)
end