Class: SFML::Shader

Inherits:
Object
  • Object
show all
Defined in:
lib/sfml/graphics/shader.rb

Overview

A GLSL shader. Build one from a vertex and/or fragment source (either a file path or a literal source string), then set uniforms with bracket assignment and pass it to draw via the shader: kwarg:

shader = SFML::Shader.from_source(fragment: "uniform sampler2D texture;\nuniform float time;\nvoid main() {\n  vec2 uv = gl_TexCoord[0].xy;\n  uv.x += sin(uv.y * 20.0 + time * 3.0) * 0.02;\n  gl_FragColor = texture2D(texture, uv) * gl_Color;\n}\n")

shader[:time] = clock.elapsed.as_seconds
window.draw(sprite, shader: shader)

Uniform types are inferred from the Ruby value:

Float / Integer / Numeric  

Array uniforms (uniform vec2 positions[8]; and friends):

[[x, y], [x, y], ...]      

Float arrays (uniform float weights[N];) are ambiguous with vec3 at length 3, so use the explicit #set_float_array setter.

Need an int / bvec / matrix uniform? Use #set_int, #set_ivec2, etc. — they exist for completeness.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#handle ⇒ Object (readonly)

:nodoc:



230
231
232
# File 'lib/sfml/graphics/shader.rb', line 230

def handle
  @handle
end

Class Method Details

.available? ⇒ Boolean

Class-level: is GLSL available on the current GPU at all?

Returns:

  • (Boolean)


44
45
46
# File 'lib/sfml/graphics/shader.rb', line 44

def self.available?
  C::Graphics.sfShader_isAvailable
end

.from_file(vertex: nil, geometry: nil, fragment: nil) ⇒ Object

Build a shader from one or more source files. Any of vertex / geometry / fragment may be omitted; at least one must be present.

Raises:



55
56
57
58
59
60
61
62
# File 'lib/sfml/graphics/shader.rb', line 55

def self.from_file(vertex: nil, geometry: nil, fragment: nil)
  _check_at_least_one(vertex, geometry, fragment)
  ptr = C::Graphics.sfShader_createFromFile(
    vertex&.to_s, geometry&.to_s, fragment&.to_s,
  )
  raise ShaderError, "sfShader_createFromFile failed (compile error or missing file?)" if ptr.null?
  _wrap(ptr)
end

.from_source(vertex: nil, geometry: nil, fragment: nil) ⇒ Object

Build a shader directly from GLSL source strings.

Raises:



65
66
67
68
69
70
# File 'lib/sfml/graphics/shader.rb', line 65

def self.from_source(vertex: nil, geometry: nil, fragment: nil)
  _check_at_least_one(vertex, geometry, fragment)
  ptr = C::Graphics.sfShader_createFromMemory(vertex, geometry, fragment)
  raise ShaderError, "sfShader_createFromMemory failed (GLSL compile error?)" if ptr.null?
  _wrap(ptr)
end

.from_stream(vertex: nil, geometry: nil, fragment: nil) ⇒ Object

Build a shader from one or more IO-like streams (any object answering read/seek/pos/size). Useful when shader source lives inside an archive or a network resource.

Raises:



75
76
77
78
79
80
81
82
# File 'lib/sfml/graphics/shader.rb', line 75

def self.from_stream(vertex: nil, geometry: nil, fragment: nil)
  _check_at_least_one(vertex, geometry, fragment)
  streams = [vertex, geometry, fragment].map { |io| io && SFML::InputStream.new(io) }
  ptrs    = streams.map { |s| s ? s.to_ptr : nil }
  ptr = C::Graphics.sfShader_createFromStream(*ptrs)
  raise ShaderError, "sfShader_createFromStream failed (GLSL compile error?)" if ptr.null?
  _wrap(ptr)
end

.geometry_available? ⇒ Boolean

Returns the self.

Returns:

  • (Boolean)


49
50
51
# File 'lib/sfml/graphics/shader.rb', line 49

def self.geometry_available?
  C::Graphics.sfShader_isGeometryAvailable
end

.unbind ⇒ Object

Returns the self.



215
# File 'lib/sfml/graphics/shader.rb', line 215

def self.unbind = C::Graphics.sfShader_bind(nil)

Instance Method Details

#[]=(name, value) ⇒ Object

Set a uniform by name. Dispatches to the right CSFML setter based on the Ruby value's type — see the class-level docs for the table.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/sfml/graphics/shader.rb', line 86

def []=(name, value)
  n = name.to_s
  case value
  when true, false
    C::Graphics.sfShader_setBoolUniform(@handle, n, value)
  when Integer
    C::Graphics.sfShader_setFloatUniform(@handle, n, value.to_f)
  when Numeric
    C::Graphics.sfShader_setFloatUniform(@handle, n, value.to_f)
  when Vector2
    v = C::System::Vector2f.new
    v[:x] = value.x.to_f; v[:y] = value.y.to_f
    C::Graphics.sfShader_setVec2Uniform(@handle, n, v)
  when Vector3
    v = C::System::Vector3f.new
    v[:x] = value.x.to_f; v[:y] = value.y.to_f; v[:z] = value.z.to_f
    C::Graphics.sfShader_setVec3Uniform(@handle, n, v)
  when Color
    C::Graphics.sfShader_setColorUniform(@handle, n, value.to_native)
  when Texture
    C::Graphics.sfShader_setTextureUniform(@handle, n, value.handle)
  when :current_texture
    C::Graphics.sfShader_setCurrentTextureUniform(@handle, n)
  when Array
    raise ArgumentError, "Shader uniform array must not be empty" if value.empty?

    first = value.first
    if first.is_a?(Array) || first.is_a?(Vector2) || first.is_a?(Vector3)
      _set_vec_array_uniform(n, value)
    else
      case value.length
      when 2 then self[name] = Vector2.new(*value)
      when 3 then self[name] = Vector3.new(*value)
      when 4
        v = C::Graphics::GlslVec4.new
        v[:x] = value[0].to_f; v[:y] = value[1].to_f
        v[:z] = value[2].to_f; v[:w] = value[3].to_f
        C::Graphics.sfShader_setVec4Uniform(@handle, n, v)
      else
        raise ArgumentError, "Shader uniform array must be length 2, 3, or 4 (got #{value.length})"
      end
    end
  else
    raise ArgumentError,
          "Shader uniform value must be Numeric, Vector2/3, Color, Texture, " \
          "Array of 2-4 numbers, or :current_texture (got #{value.class})"
  end
end

#bind ⇒ Object

Bind this shader as the active GL program. Useful when you want to issue raw GL draw calls under SFML's context. Pair with Shader.unbind to restore SFML's default. Most users don't need this — just pass the shader to target.draw(..., render_states: SFML::RenderStates.new(shader: self)).



212
# File 'lib/sfml/graphics/shader.rb', line 212

def bind = C::Graphics.sfShader_bind(@handle)

#native_handle ⇒ Object

The OpenGL program ID. Useful for debug printf / interop with raw GL libraries.



219
# File 'lib/sfml/graphics/shader.rb', line 219

def native_handle = C::Graphics.sfShader_getNativeHandle(@handle)

#set_bvec(name, *components) ⇒ Object

Set a uniform bvec2/3/4 from a Ruby Array of booleans. Length picks the dimensionality.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/sfml/graphics/shader.rb', line 158

def set_bvec(name, *components)
  flat = components.flatten
  case flat.length
  when 2
    s = C::Graphics::GlslBvec2.new
    s[:x] = !!flat[0]; s[:y] = !!flat[1]
    C::Graphics.sfShader_setBvec2Uniform(@handle, name.to_s, s)
  when 3
    s = C::Graphics::GlslBvec3.new
    s[:x] = !!flat[0]; s[:y] = !!flat[1]; s[:z] = !!flat[2]
    C::Graphics.sfShader_setBvec3Uniform(@handle, name.to_s, s)
  when 4
    s = C::Graphics::GlslBvec4.new
    s[:x] = !!flat[0]; s[:y] = !!flat[1]; s[:z] = !!flat[2]; s[:w] = !!flat[3]
    C::Graphics.sfShader_setBvec4Uniform(@handle, name.to_s, s)
  else
    raise ArgumentError, "bvec uniform must have 2, 3, or 4 components (got #{flat.length})"
  end
end

#set_float_array(name, values) ⇒ Object

Set a uniform float arr[N]; from a plain Ruby array of numbers. Float arrays can't be inferred via []= because they'd collide with the vec3 case at length 3.



150
151
152
153
154
# File 'lib/sfml/graphics/shader.rb', line 150

def set_float_array(name, values)
  buf = FFI::MemoryPointer.new(:float, values.length)
  buf.write_array_of_float(values.map(&:to_f))
  C::Graphics.sfShader_setFloatUniformArray(@handle, name.to_s, buf, values.length)
end

#set_int(name, value) ⇒ Object

Explicit integer setters when a uniform really is uniform int n.



136
137
138
# File 'lib/sfml/graphics/shader.rb', line 136

def set_int(name, value)
  C::Graphics.sfShader_setIntUniform(@handle, name.to_s, Integer(value))
end

#set_int_color(name, color) ⇒ Object

Set a vec4 uniform from an integer-channel SFML::Color (RGBA 0–255). Equivalent to writing [c.r/255, ..., c.a/255] by hand into a vec4 — the CSFML helper does the divide for you.

Raises:

  • (ArgumentError)


225
226
227
228
# File 'lib/sfml/graphics/shader.rb', line 225

def set_int_color(name, color)
  raise ArgumentError, "expected SFML::Color" unless color.is_a?(Color)
  C::Graphics.sfShader_setIntColorUniform(@handle, name.to_s, color.to_native)
end

#set_ivec2(name, x, y) ⇒ Object

Returns the ivec2.



141
142
143
144
145
# File 'lib/sfml/graphics/shader.rb', line 141

def set_ivec2(name, x, y)
  v = C::System::Vector2i.new
  v[:x] = Integer(x); v[:y] = Integer(y)
  C::Graphics.sfShader_setIvec2Uniform(@handle, name.to_s, v)
end

#set_mat3(name, matrix) ⇒ Object

Set a uniform mat3 from a 9-element row-major float array (or an SFML::Transform — its 3×3 matrix is read directly).



180
181
182
183
184
185
# File 'lib/sfml/graphics/shader.rb', line 180

def set_mat3(name, matrix)
  values = _coerce_matrix(matrix, 9, "mat3")
  mat = C::Graphics::GlslMat3.new
  values.each_with_index { |v, i| mat[:array][i] = v }
  C::Graphics.sfShader_setMat3Uniform(@handle, name.to_s, mat.pointer)
end

#set_mat3_array(name, matrices) ⇒ Object

Set a uniform mat3 arr[N]; / uniform mat4 arr[N]; from a list of matrices (each a 9- or 16-element flat array, or an SFML::Transform for mat3).



198
199
200
# File 'lib/sfml/graphics/shader.rb', line 198

def set_mat3_array(name, matrices)
  _set_mat_array(name, matrices, 9, :sfShader_setMat3UniformArray)
end

#set_mat4(name, matrix) ⇒ Object

Set a uniform mat4 from a 16-element row-major float array.



188
189
190
191
192
193
# File 'lib/sfml/graphics/shader.rb', line 188

def set_mat4(name, matrix)
  values = _coerce_matrix(matrix, 16, "mat4")
  mat = C::Graphics::GlslMat4.new
  values.each_with_index { |v, i| mat[:array][i] = v }
  C::Graphics.sfShader_setMat4Uniform(@handle, name.to_s, mat.pointer)
end

#set_mat4_array(name, matrices) ⇒ Object

Returns the mat4 array.



203
204
205
# File 'lib/sfml/graphics/shader.rb', line 203

def set_mat4_array(name, matrices)
  _set_mat_array(name, matrices, 16, :sfShader_setMat4UniformArray)
end