lib/mittsu/renderers/opengl/opengl_program.rb in mittsu-0.1.1 vs lib/mittsu/renderers/opengl/opengl_program.rb in mittsu-0.1.2
- old
+ new
@@ -1,286 +1,192 @@
+require 'erb'
+
require 'mittsu/renderers/opengl/opengl_shader'
module Mittsu
class OpenGLProgram
attr_reader :id, :program, :uniforms, :attributes
attr_accessor :code, :used_times, :attributes, :vertex_shader, :fragment_shader
def initialize(renderer, code, material, parameters)
- defines = material.defines || {} # TODO: setting to default object... ?
- uniforms = material[:_opengl_shader][:uniforms]
- attributes = material.attributes || [] # TODO: setting to default array... ?
+ @id = (@@id ||= 1).tap { @@id += 1 }
+ @renderer = renderer
- vertex_shader = material[:_opengl_shader][:vertex_shader]
- fragment_shader = material[:_opengl_shader][:fragment_shader]
+ compile_and_link_program(material, parameters)
+ material_impl = material.implementation(@renderer)
+ cache_uniform_locations(material_impl.shader[:uniforms] || {}, parameters)
+ cache_attribute_locations(material.attributes || {}, parameters)
+
+ @code = code
+ @used_times = 2
+ end
+
+ private
+
+ def compile_and_link_program(material, parameters)
+ @program = glCreateProgram
+
# TODO: necessary for OpenGL?
# index0_attribute_name = material.index0_attribute_name
#
# if index0_attribute_name.nil? && parameters[:morph_targets]
# # programs with morph_targets displace position of attribute 0
#
# index0_attribute_name = 'position'
# end
- shadow_map_type_define = 'SHADOWMAP_TYPE_BASIC'
+ compile_shaders(material, parameters)
- if parameters[:shadow_map_type] == PCFShadowMap
- shadow_map_type_define = 'SHADOWMAP_TYPE_PCF_SOFT'
- elsif parameters[:shadow_map_type] == PCFSoftShadowMap
- shadow_map_type_define = 'SHADOWMAP_TYPE_PCF_SOFT'
- end
+ # if !index0_attribute_name.nil?
+ # TODO: is this necessary in OpenGL ???
+ # Force a particular attribute to index 0.
+ # because potentially expensive emulation is done by __browser__ if attribute 0 is disabled. (no browser here!)
+ # And, color, for example is often automatically bound to index 0 so disabling it
- env_map_type_define = 'ENVMAP_TYPE_CUBE'
- env_map_mode_define = 'ENVMAP_MODE_REFLECTION'
- env_map_blending_define = 'ENVMAP_BLENDING_MULTIPLY'
+ # glBindAttributeLocation(program, 0, index0_attribute_name)
+ # end
- if parameters[:env_map]
- case material.env_map.mapping
- when CubeReflectionMapping, CubeRefractionMapping
- env_map_type_define = 'ENVMAP_TYPE_CUBE'
- when EquirectangularReflectionMapping, EquirectangularRefractionMapping
- env_map_type_define = 'ENVMAP_TYPE_EQUIREC'
- when SphericalReflectionMapping
- env_map_type_define = 'ENVMAP_TYPE_SPHERE'
- end
+ glLinkProgram(@program)
+ check_for_link_errors
+ post_link_clean_up
+ end
- case material.env_map.mapping
- when CubeRefractionMapping, EquirectangularRefractionMapping
- env_map_mode_define 'ENVMAP_MODE_REFRACTION'
- end
+ def generate_defines(defines)
+ chunks = []
- case material.combine
- when MultiplyOperation
- env_map_blending_define = 'ENVMAP_BLENDING_MULTIPLY'
- when MixOperation
- env_map_blending_define = 'ENVMAP_BLENDING_MIX'
- when AddOperation
- env_map_blending_define = 'ENVMAP_BLENDING_ADD'
- end
+ defines.each do |d, value|
+ next if value == false
+
+ chunk = "#define #{d} #{value}"
+ chunks << chunk
end
- gamma_factor_define = (renderer.gamma_factor > 0) ? renderer.gamma_factor : 1.0
+ chunks.join("\n")
+ end
- # puts 'building new program'
+ def link_status
+ ptr = ' '*8
+ glGetProgramiv @program, GL_LINK_STATUS, ptr
+ ptr.unpack('L')[0]
+ end
- #
+ def program_info_log
+ ptr = ' '*8
+ glGetProgramiv @program, GL_INFO_LOG_LENGTH, ptr
+ length = ptr.unpack('L')[0]
- custom_defines = generate_defines(defines)
-
- #
-
- @program = glCreateProgram
-
- if false # material.is_a?(RawShaderMaterial) # TODO: when RawShaderMaterial exists
- prefix_vertex = ''
- prefix_fragment = ''
+ if length > 0
+ log = ' '*length
+ glGetProgramInfoLog @program, length, ptr, log
+ log.unpack("A#{length}")[0]
else
- prefix_vertex = [
- '#version 330',
- # TODO: do we need precision for an OpenGL program?
- # "precision #{parameters[:precision]} float;",
- # "precision #{parameters[:precision]} int;",
+ ''
+ end
+ end
- custom_defines,
+ def check_for_link_errors
+ log_info = program_info_log
- parameters[:supports_vertex_textures] ? '#define VERTEX_TEXTURES' : '',
+ if !link_status
+ puts "ERROR: Mittsu::OpenGLProgram: shader error: #{glGetError}, GL_INVALID_STATUS, #{glGetProgramParameter(program, GL_VALIDATE_STATUS)}, glGetProgramParameterInfoLog, #{log_info}"
+ end
- renderer.gamma_input ? '#define GAMMA_INPUT' : '',
- renderer.gamma_output ? '#define GAMMA_OUTPUT' : '',
- "#define GAMMA_FACTOR #{gamma_factor_define}",
+ if !log_info.empty?
+ puts "WARNING: Mittsu::OpenGLProgram: glGetProgramInfoLog, #{log_info}"
+ # TODO: useless in OpenGL ???
+ # puts "WARNING: #{glGetExtension( 'OPENGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader )}"
+ # puts "WARNING: #{glGetExtension( 'OPENGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader )}"
+ end
+ end
- "#define MAX_DIR_LIGHTS #{parameters[:max_dir_lights]}",
- "#define MAX_POINT_LIGHTS #{parameters[:max_point_lights]}",
- "#define MAX_SPOT_LIGHTS #{parameters[:max_spot_lights]}",
- "#define MAX_HEMI_LIGHTS #{parameters[:max_hemi_lights]}",
+ def get_shadow_map_define(shadow_map_param)
+ case shadow_map_param
+ when PCFShadowMap
+ 'SHADOWMAP_TYPE_PCF'
+ when PCFSoftShadowMap
+ 'SHADOWMAP_TYPE_PCF_SOFT'
+ else
+ 'SHADOWMAP_TYPE_BASIC'
+ end
+ end
- "#define MAX_SHADOWS #{parameters[:max_shadows]}",
+ def get_env_map_type_define(env_map_param, material)
+ return 'ENVMAP_TYPE_CUBE' unless env_map_param
+ case material.env_map.mapping
+ when CubeReflectionMapping, CubeRefractionMapping
+ 'ENVMAP_TYPE_CUBE'
+ when EquirectangularReflectionMapping, EquirectangularRefractionMapping
+ 'ENVMAP_TYPE_EQUIREC'
+ when SphericalReflectionMapping
+ 'ENVMAP_TYPE_SPHERE'
+ else
+ 'ENVMAP_TYPE_CUBE'
+ end
+ end
- "#define MAX_BONES #{parameters[:max_bones]}",
+ def get_env_map_mode_define(env_map_param, material)
+ return 'ENVMAP_MODE_REFLECTION' unless env_map_param
+ case material.env_map.mapping
+ when CubeRefractionMapping, EquirectangularRefractionMapping
+ 'ENVMAP_MODE_REFRACTION'
+ else
+ 'ENVMAP_MODE_REFLECTION'
+ end
+ end
- parameters[:map] ? '#define USE_MAP' : '',
- parameters[:env_map] ? '#define USE_ENVMAP' : '',
- parameters[:env_map] ? "#define #{env_map_mode_define}" : '',
- parameters[:light_map] ? '#define USE_LIGHTMAP' : '',
- parameters[:bump_map] ? '#define USE_BUMPMAP' : '',
- parameters[:normal_map] ? '#define USE_NORMALMAP' : '',
- parameters[:specular_map] ? '#define USE_SPECULARMAP' : '',
- parameters[:alpha_map] ? '#define USE_ALPHAMAP' : '',
- parameters[:vertex_colors] ? '#define USE_COLOR' : '',
-
- parameters[:flat_shading] ? '#define FLAT_SHADED': '',
-
- parameters[:skinning] ? '#define USE_SKINNING' : '',
- parameters[:use_vertex_texture] ? '#define BONE_TEXTURE' : '',
-
- parameters[:morph_targets] ? '#define USE_MORPHTARGETS' : '',
- parameters[:morph_normals] ? '#define USE_MORPHNORMALS' : '',
- parameters[:wrap_around] ? '#define WRAP_AROUND' : '',
- parameters[:double_sided] ? '#define DOUBLE_SIDED' : '',
- parameters[:flip_sided] ? '#define FLIP_SIDED' : '',
-
- parameters[:shadow_map_enabled] ? '#define USE_SHADOWMAP' : '',
- parameters[:shadow_map_enabled] ? "#define #{shadow_map_type_define}" : '',
- parameters[:shadow_map_debug] ? '#define SHADOWMAP_DEBUG' : '',
- parameters[:shadow_map_cascade] ? '#define SHADOWMAP_CASCADE' : '',
-
- parameters[:size_attenuation] ? '#define USE_SIZEATTENUATION' : '',
-
- parameters[:logarithmic_depth_buffer] ? '#define USE_LOGDEPTHBUF' : '',
- #renderer._glExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '',
-
-
- 'uniform mat4 modelMatrix;',
- 'uniform mat4 modelViewMatrix;',
- 'uniform mat4 projectionMatrix;',
- 'uniform mat4 viewMatrix;',
- 'uniform mat3 normalMatrix;',
- 'uniform vec3 cameraPosition;',
-
- 'in vec3 position;',
- 'in vec3 normal;',
- 'in vec2 uv;',
- 'in vec2 uv2;',
-
- '#ifdef USE_COLOR',
-
- ' in vec3 color;',
-
- '#endif',
-
- '#ifdef USE_MORPHTARGETS',
-
- ' in vec3 morphTarget0;',
- ' in vec3 morphTarget1;',
- ' in vec3 morphTarget2;',
- ' in vec3 morphTarget3;',
-
- ' #ifdef USE_MORPHNORMALS',
-
- ' in vec3 morphNormal0;',
- ' in vec3 morphNormal1;',
- ' in vec3 morphNormal2;',
- ' in vec3 morphNormal3;',
-
- ' #else',
-
- ' in vec3 morphTarget4;',
- ' in vec3 morphTarget5;',
- ' in vec3 morphTarget6;',
- ' in vec3 morphTarget7;',
-
- ' #endif',
-
- '#endif',
-
- '#ifdef USE_SKINNING',
-
- ' in vec4 skinIndex;',
- ' in vec4 skinWeight;',
-
- '#endif',
- ].reject(&:empty?).join("\n") + "\n"
-
- prefix_fragment = [
- '#version 330',
- # TODO: do we need precision for an OpenGL program?
- # "precison #{parameters[:precision]} float;",
- # "precison #{parameters[:precision]} int;",
-
- # (parameters[:bump_map] || parameters[:normal_map] || parameters[:flat_shading]) ? '#extension GL_OES_standard_derivatives : enable' : '', # TODO: oes extension in OpenGL?
-
- custom_defines,
-
- "#define MAX_DIR_LIGHTS #{parameters[:max_dir_lights]}",
- "#define MAX_POINT_LIGHTS #{parameters[:max_point_lights]}",
- "#define MAX_SPOT_LIGHTS #{parameters[:max_spot_lights]}",
- "#define MAX_HEMI_LIGHTS #{parameters[:max_hemi_lights]}",
-
- "#define MAX_SHADOWS #{parameters[:max_shadows]}",
-
- parameters[:alpha_test] ? "#define ALPHATEST #{parameters[:alpha_test].to_f}" : '',
-
- renderer.gamma_input ? '#define GAMMA_INPUT' : '',
- renderer.gamma_output ? '#define GAMMA_OUTPUT' : '',
- "#define GAMMA_FACTOR #{gamma_factor_define}",
-
- (parameters[:use_fog] && parameters[:fog]) ? '#define USE_FOG' : '',
- (parameters[:use_fog] && parameters[:fog_exp]) ? '#define FOG_EXP2' : '',
-
- parameters[:map] ? '#define USE_MAP' : '',
- parameters[:env_map] ? '#define USE_ENVMAP' : '',
- parameters[:env_map] ? "#define #{env_map_type_define}" : '',
- parameters[:env_map] ? "#define #{env_map_mode_define}" : '',
- parameters[:env_map] ? "#define #{env_map_blending_define}" : '',
- parameters[:light_map] ? '#define USE_LIGHTMAP' : '',
- parameters[:bump_map] ? '#define USE_BUMPMAP' : '',
- parameters[:normal_map] ? '#define USE_NORMALMAP' : '',
- parameters[:specular_map] ? '#define USE_SPECULARMAP' : '',
- parameters[:alpha_map] ? '#define USE_ALPHAMAP' : '',
- parameters[:vertex_colors] ? '#define USE_COLOR' : '',
-
- parameters[:flat_shading] ? '#define FLAT_SHADED' : '',
-
- parameters[:metal] ? '#define METAL' : '',
- parameters[:wrap_around] ? '#define WRAP_AROUND' : '',
- parameters[:double_sided] ? '#define DOUBLE_SIDED' : '',
- parameters[:flip_sided] ? '#define FLIP_SIDED' : '',
-
-
- parameters[:shadow_map_enabled] ? '#define USE_SHADOWMAP' : '',
- parameters[:shadow_map_enabled] ? "#define #{shadow_map_type_define}" : '',
- parameters[:shadow_map_debug] ? '#define SHADOWMAP_DEBUG' : '',
- parameters[:'shadow_map_cascade'] ? '#define SHADOWMAP_CASCADE' : '',
-
- parameters[:logarithmic_depth_buffer] ? '#define USE_LOGDEPTHBUF' : '',
- #renderer._glExtensionFragDepth ? '#define USE_LOGDEPTHBUF_EXT' : '',
-
- 'uniform mat4 viewMatrix;',
- 'uniform vec3 cameraPosition;',
-
- 'layout(location = 0) out vec4 fragColor;'
- ].reject(&:empty?).join("\n") + "\n"
+ def get_env_map_blending_define(env_map_param, material)
+ return 'ENVMAP_BLENDING_MULTIPLY' unless env_map_param
+ case material.combine
+ when MultiplyOperation
+ 'ENVMAP_BLENDING_MULTIPLY'
+ when MixOperation
+ 'ENVMAP_BLENDING_MIX'
+ when AddOperation
+ 'ENVMAP_BLENDING_ADD'
+ else
+ 'ENVMAP_BLENDING_MULTIPLY'
end
+ end
- gl_vertex_shader = OpenGLShader.new(GL_VERTEX_SHADER, prefix_vertex + vertex_shader)
- gl_fragment_shader = OpenGLShader.new(GL_FRAGMENT_SHADER, prefix_fragment + fragment_shader)
+ def compile_shaders(material, parameters)
+ material_impl = material.implementation(@renderer)
+ shadow_map_type_define = get_shadow_map_define(parameters[:shadow_map_type])
- glAttachShader(@program, gl_vertex_shader.shader)
- glAttachShader(@program, gl_fragment_shader.shader)
+ env_map_type_define = get_env_map_type_define(parameters[:env_map], material)
+ env_map_mode_define = get_env_map_mode_define(parameters[:env_map], material)
+ env_map_blending_define = get_env_map_blending_define(parameters[:env_map], material)
- # if !index0_attribute_name.nil?
- # TODO: is this necessary in OpenGL ???
- # Force a particular attribute to index 0.
- # because potentially expensive emulation is done by __browser__ if attribute 0 is disabled. (no browser here!)
- # And, color, for example is often automatically bound to index 0 so disabling it
+ gamma_factor_define = (@renderer.gamma_factor > 0) ? @renderer.gamma_factor : 1.0
- # glBindAttributeLocation(program, 0, index0_attribute_name)
- # end
+ custom_defines = generate_defines(material.defines || {})
- glLinkProgram(@program)
-
- log_info = program_info_log
-
- if !link_status
- puts "ERROR: Mittsu::OpenGLProgram: shader error: #{glGetError}, GL_INVALID_STATUS, #{glGetProgramParameter(program, GL_VALIDATE_STATUS)}, glGetProgramParameterInfoLog, #{program_log_info}"
+ if false # material.is_a?(RawShaderMaterial) # TODO: when RawShaderMaterial exists
+ prefix_vertex = ''
+ prefix_fragment = ''
+ else
+ prefix_vertex = File.read(File.expand_path('../../shaders/shader_templates/vertex.glsl.erb', __FILE__))
+ prefix_fragment = File.read(File.expand_path('../../shaders/shader_templates/fragment.glsl.erb', __FILE__))
end
- if !log_info.empty?
- puts "WARNING: Mittsu::OpenGLProgram: glGetProgramInfoLog, #{log_info}"
- # TODO: useless in OpenGL ???
- # puts "WARNING: #{glGetExtension( 'OPENGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader )}"
- # puts "WARNING: #{glGetExtension( 'OPENGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader )}"
- end
+ @vertex_shader = OpenGLShader.new(GL_VERTEX_SHADER, compile_shader_template(prefix_vertex + material_impl.shader[:vertex_shader], binding))
+ @fragment_shader = OpenGLShader.new(GL_FRAGMENT_SHADER, compile_shader_template(prefix_fragment + material_impl.shader[:fragment_shader], binding))
- # clean up
+ glAttachShader(@program, @vertex_shader.shader)
+ glAttachShader(@program, @fragment_shader.shader)
+ end
- glDeleteShader(gl_vertex_shader.shader)
- glDeleteShader(gl_fragment_shader.shader)
+ def compile_shader_template(template, b)
+ ERB.new(template).result(b)
+ end
- # cache uniform locations
+ def post_link_clean_up
+ glDeleteShader(@vertex_shader.shader)
+ glDeleteShader(@fragment_shader.shader)
+ end
+ def cache_uniform_locations(uniforms, parameters)
identifiers = [
'viewMatrix',
'modelViewMatrix',
'projectionMatrix',
'normalMatrix',
@@ -305,14 +211,17 @@
uniforms.each do |k, v|
identifiers << k.to_s
end
- @uniforms = cache_uniform_locations(program, identifiers)
+ @uniforms = {}
+ identifiers.each do |id|
+ @uniforms[id] = glGetUniformLocation(program, id)
+ end
+ end
- # cache attributes locations
-
+ def cache_attribute_locations(attributes, parameters)
identifiers = [
'position',
'normal',
'uv',
'uv2',
@@ -333,70 +242,12 @@
attributes.each do |k, v|
identifiers << k
end
- @attributes = cache_attribute_locations(program, identifiers)
-
- @id = (@@id ||= 1).tap { @@id += 1 }
- @code = code
- @used_times = 2
- @vertex_shader = gl_vertex_shader
- @fragment_shader = gl_fragment_shader
- end
-
- private
-
- def generate_defines(defines)
- chunks = []
-
- defines.each do |d, value|
- next if value == false
-
- chunk = "#define #{d} #{value}"
- chunks << chunk
- end
-
- chunks.join("\n")
- end
-
- def cache_uniform_locations(program, identifiers)
- uniforms = {}
-
+ @attributes = {}
identifiers.each do |id|
- uniforms[id] = glGetUniformLocation(program, id)
- end
-
- uniforms
- end
-
- def cache_attribute_locations(program, identifiers)
- attributes = {}
-
- identifiers.each do |id|
- attributes[id] = glGetAttribLocation(program, id)
- end
-
- attributes
- end
-
- def link_status
- ptr = ' '*8
- glGetProgramiv @program, GL_LINK_STATUS, ptr
- ptr.unpack('L')[0]
- end
-
- def program_info_log
- ptr = ' '*8
- glGetProgramiv @program, GL_INFO_LOG_LENGTH, ptr
- length = ptr.unpack('L')[0]
-
- if length > 0
- log = ' '*length
- glGetProgramInfoLog @program, length, ptr, log
- log.unpack("A#{length}")[0]
- else
- ''
+ @attributes[id] = glGetAttribLocation(program, id)
end
end
end
end