From BlenderWiki

Jump to: navigation, search

[edit] Pynode GLSL

This is the first step towards accommodating GLSL code snippets definition in PyNodes. Which in turn is related to the proposal for the viewport plan of action.

The overall idea is to allow for external engines to define an optional GLSL code for each node. This would automatically be picked up by the Eevee engine. Also, this would allow for external engines to benefit from Blender’s internal GLSL code parser, for their own viewport drawings.

[edit] Cycles Engine

class CyclesRender(bpy.types.RenderEngine):
	# Define whether or not to use OpenGL/Vulkan API
	bl_use_realtime_engine = False
	# This string will be added at to all shaders
	# Put redundant GLSL code here
	bl_gpu_library = """
		#define CYCLES_M_PI 3.14
		vec3 CYCLES_to_tangent(...) {...};
		float CYCLES_saturate(...) {...};

The bl_gpu_library contains all functions shared between nodes to avoid code duplication. All functions, variables and constants should have a prefix to avoid namespace conflicts later.

[edit] Cycles Nodes

class CyclesMixShaderNode(bpy.types.ShaderNode):
	# with which render engine this node will be executed
	bl_compatibility = {'CYCLES'}
	# Code is defined here in case any realtime RenderEngines want to use it
	# Multiple functions can define in there
	bl_gpu_code = {'CYCLES' : "void node_shader_mix_shader(...) {out_col = ...}"}
	# Logic to call GLSL functions for the node itself
	def gpu_exec(self, ins, outs):
		return gpu_stack_link('node_shader_mix_shader', ins, outs)

Note : only bpy.types.ShaderNode will inherit theses properties.

This means we need to port all cycles nodes to this API. The gpu_exec function should be a direct port from the gpu_shader_*** functions defined in source/blender/nodes/shader/nodes/node_shader_*. The bl_gpu_code will be the functions needed by this node that are in source/blender/gpu/shaders/gpu_shader_material.glsl The bl_gpu_code will be only added once per shader.

The compatibility flag is here to prune any node not compatible with the engine before parsing the tree. It is not only for realtime engines but also for offline engines. Any node not compatible with the current Engine will be clearly indicated to the user (gray out, ...).

Any node that does not have GLSL code will be considered incompatible to realtime engines.

[edit] Other Engine

Now we will take a look at how a new engine is implemented. For this exemple, we use the future Eevee Engine.

import bpy
class EeveeRender(RenderEngine):
	# Enable OpenGL/Vulkan rendering
	bl_use_realtime_engine = True
	# Enable Internal Features
	bl_gpu_features = {'PROBES', 'SHADOWS', 'LIGHTS'...}
	# Define it's own library
	bl_gpu_library = """
		#define EEVEE_M_PI 3.14
		vec3 EEVEE_to_tangent(...)
	# Convertion functions
	bl_gpu_converts ="""
		vec4 float_to_vec4(in float val) {return vec4(val);}
def register():
	# This line add 'EEVEE' to the bl_compatibility of CyclesMixShaderNode
	# and reuse Cycles GLSL code
	add_engine_compatibility('EEVEE', bpy.types.ShaderNodeMixShader)
	# You can also specify you own glsl for the node
	add_engine_compatibility('EEVEE', bpy.types.ShaderNodeMixShader, "Custom GLSL Code here")
	# Reconstruct the GLSL hash after all compatibility changes
def unregister():
	remove_engine_compatibility('EEVEE', bpy.types.ShaderNodeType)

The bl_gpu_converts is mandatory to convert i/o sockets in the tree. If a link use an undefined conversion, we let the shader not compile and a use default one.

After parsing, the shader code will look like something like this.

[edit] Example 1

Running Eevee, with 2 eevee nodes.



void main(){
 	gl_FragColor = c;

Only one library and 2 gpu_code are loaded.

[edit] Example 2 :

Running Eevee, with 1 eevee node and 1 compatible cycles node.




void main(){
 	gl_FragColor = c;

This time the 2 libraries are needed because we are using code from the 2 engines.