multiple effects
This commit is contained in:
237
film2d.cm
237
film2d.cm
@@ -133,79 +133,48 @@ function process_scene_tree(node, camera, ctx, parent_tint, parent_opacity, pare
|
||||
return collect_drawables(node, camera, parent_tint, parent_opacity, parent_scissor, parent_pos, ctx)
|
||||
}
|
||||
|
||||
// Process a group with effects - creates an "island" if needed
|
||||
// Process a group with effects - creates an "island" and applies effects sequentially
|
||||
function process_effect_group(node, camera, ctx, parent_tint, parent_opacity, parent_scissor, parent_pos) {
|
||||
var effects = node.effects
|
||||
var drawables = []
|
||||
var backend = ctx.backend
|
||||
var target_size = ctx.target_size
|
||||
|
||||
// For each effect, determine if we need an island
|
||||
// 1. Render all children to an initial content target
|
||||
var current_target = backend.get_or_create_target(target_size.width, target_size.height, 'effect_start_' + ctx.target_counter++)
|
||||
var child_drawables = collect_children_drawables(node, camera, parent_tint, parent_opacity, parent_scissor, parent_pos, ctx)
|
||||
|
||||
render_drawables_to_target(child_drawables, current_target, camera, ctx)
|
||||
|
||||
// 2. Apply effects sequentially, each one consuming the previous target and producing a new one
|
||||
for (var effect of effects) {
|
||||
var effect_type = effect.type
|
||||
|
||||
if (effect_type == 'bloom') {
|
||||
// Bloom requires an island - render children to intermediate, apply bloom, return as single drawable
|
||||
var island_result = render_bloom_island(node, camera, ctx, effect, parent_tint, parent_opacity, parent_scissor, parent_pos)
|
||||
if (island_result) {
|
||||
drawables.push(island_result)
|
||||
}
|
||||
return drawables
|
||||
}
|
||||
|
||||
if (effect_type == 'mask') {
|
||||
// Mask - check if soft (needs texture) or hard (can use stencil)
|
||||
var soft = effect.soft || false
|
||||
if (soft) {
|
||||
// Soft mask needs island
|
||||
var island_result = render_mask_island(node, camera, ctx, effect, parent_tint, parent_opacity, parent_scissor, parent_pos)
|
||||
if (island_result) {
|
||||
drawables.push(island_result)
|
||||
}
|
||||
return drawables
|
||||
} else {
|
||||
// Hard mask - can potentially use stencil inline
|
||||
// For now, still use island approach for simplicity
|
||||
var island_result = render_mask_island(node, camera, ctx, effect, parent_tint, parent_opacity, parent_scissor, parent_pos)
|
||||
if (island_result) {
|
||||
drawables.push(island_result)
|
||||
}
|
||||
return drawables
|
||||
}
|
||||
}
|
||||
|
||||
if (effect_type == 'blur') {
|
||||
// Blur requires an island
|
||||
var island_result = render_blur_island(node, camera, ctx, effect, parent_tint, parent_opacity, parent_scissor, parent_pos)
|
||||
if (island_result) {
|
||||
drawables.push(island_result)
|
||||
}
|
||||
return drawables
|
||||
current_target = apply_bloom_effect(current_target, effect, ctx)
|
||||
} else if (effect_type == 'mask') {
|
||||
current_target = apply_mask_effect(current_target, effect, ctx)
|
||||
} else if (effect_type == 'blur') {
|
||||
current_target = apply_blur_effect(current_target, effect, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown effects - just render children normally
|
||||
return collect_drawables(node, camera, parent_tint, parent_opacity, parent_scissor, parent_pos, ctx)
|
||||
// 3. Return a single drawable that blits the final result
|
||||
return [{
|
||||
type: 'blit_target',
|
||||
layer: node.layer || 0,
|
||||
world_y: 0,
|
||||
target: current_target,
|
||||
width: target_size.width,
|
||||
height: target_size.height
|
||||
}]
|
||||
}
|
||||
|
||||
// Render bloom effect as an island
|
||||
function render_bloom_island(node, camera, ctx, effect, parent_tint, parent_opacity, parent_scissor, parent_pos) {
|
||||
var backend = ctx.backend
|
||||
var target_size = ctx.target_size
|
||||
|
||||
// Allocate targets for the island
|
||||
var content_target = backend.get_or_create_target(target_size.width, target_size.height, 'bloom_content_' + ctx.target_counter++)
|
||||
var threshold_target = backend.get_or_create_target(target_size.width, target_size.height, 'bloom_threshold_' + ctx.target_counter++)
|
||||
var blur_target_a = backend.get_or_create_target(target_size.width, target_size.height, 'bloom_blur_a_' + ctx.target_counter++)
|
||||
var blur_target_b = backend.get_or_create_target(target_size.width, target_size.height, 'bloom_blur_b_' + ctx.target_counter++)
|
||||
var output_target = backend.get_or_create_target(target_size.width, target_size.height, 'bloom_output_' + ctx.target_counter++)
|
||||
|
||||
// Collect children drawables
|
||||
var child_drawables = collect_children_drawables(node, camera, parent_tint, parent_opacity, parent_scissor, parent_pos, ctx)
|
||||
|
||||
// Render children to content target
|
||||
ctx.commands.push({cmd: 'begin_render', target: content_target, clear: {r: 0, g: 0, b: 0, a: 0}})
|
||||
// Helper: Render a list of drawables to a specific target
|
||||
function render_drawables_to_target(drawables, target, camera, ctx) {
|
||||
ctx.commands.push({cmd: 'begin_render', target: target, clear: {r: 0, g: 0, b: 0, a: 0}})
|
||||
ctx.commands.push({cmd: 'set_camera', camera: camera})
|
||||
|
||||
var batches = batch_drawables(child_drawables)
|
||||
var batches = batch_drawables(drawables)
|
||||
for (var batch of batches) {
|
||||
if (batch.type == 'sprite_batch') {
|
||||
ctx.commands.push({
|
||||
@@ -217,15 +186,42 @@ function render_bloom_island(node, camera, ctx, effect, parent_tint, parent_opac
|
||||
})
|
||||
} else if (batch.type == 'text') {
|
||||
ctx.commands.push({cmd: 'draw_text', drawable: batch.drawable})
|
||||
} else if (batch.type == 'particles') {
|
||||
ctx.commands.push({
|
||||
cmd: 'draw_batch',
|
||||
batch_type: 'particles',
|
||||
geometry: {sprites: batch.sprites},
|
||||
texture: batch.texture,
|
||||
material: batch.material
|
||||
})
|
||||
} else if (batch.type == 'blit_target') {
|
||||
ctx.commands.push({
|
||||
cmd: 'blit',
|
||||
texture: batch.drawable.target,
|
||||
target: target,
|
||||
dst_rect: {x: 0, y: 0, width: batch.drawable.width, height: batch.drawable.height},
|
||||
filter: 'linear'
|
||||
})
|
||||
}
|
||||
}
|
||||
ctx.commands.push({cmd: 'end_render'})
|
||||
}
|
||||
|
||||
// Apply bloom effect to a source target, returning a new target
|
||||
function apply_bloom_effect(src_target, effect, ctx) {
|
||||
var backend = ctx.backend
|
||||
var target_size = ctx.target_size
|
||||
|
||||
var threshold_target = backend.get_or_create_target(target_size.width, target_size.height, 'bloom_threshold_' + ctx.target_counter++)
|
||||
var blur_target_a = backend.get_or_create_target(target_size.width, target_size.height, 'bloom_blur_a_' + ctx.target_counter++)
|
||||
var blur_target_b = backend.get_or_create_target(target_size.width, target_size.height, 'bloom_blur_b_' + ctx.target_counter++)
|
||||
var output_target = backend.get_or_create_target(target_size.width, target_size.height, 'bloom_output_' + ctx.target_counter++)
|
||||
|
||||
// Threshold pass
|
||||
ctx.commands.push({
|
||||
cmd: 'shader_pass',
|
||||
shader: 'threshold',
|
||||
input: content_target,
|
||||
input: src_target,
|
||||
output: threshold_target,
|
||||
uniforms: {threshold: effect.threshold || 0.8, intensity: effect.intensity || 1.0}
|
||||
})
|
||||
@@ -236,7 +232,6 @@ function render_bloom_island(node, camera, ctx, effect, parent_tint, parent_opac
|
||||
var blur_src = threshold_target
|
||||
|
||||
for (var i = 0; i < blur_passes; i++) {
|
||||
// Horizontal blur
|
||||
ctx.commands.push({
|
||||
cmd: 'shader_pass',
|
||||
shader: 'blur',
|
||||
@@ -244,8 +239,6 @@ function render_bloom_island(node, camera, ctx, effect, parent_tint, parent_opac
|
||||
output: blur_target_a,
|
||||
uniforms: {direction: [2, 0], texel_size: texel_size}
|
||||
})
|
||||
|
||||
// Vertical blur
|
||||
ctx.commands.push({
|
||||
cmd: 'shader_pass',
|
||||
shader: 'blur',
|
||||
@@ -253,146 +246,62 @@ function render_bloom_island(node, camera, ctx, effect, parent_tint, parent_opac
|
||||
output: blur_target_b,
|
||||
uniforms: {direction: [0, 2], texel_size: texel_size}
|
||||
})
|
||||
|
||||
blur_src = blur_target_b
|
||||
}
|
||||
|
||||
// Composite bloom back onto content
|
||||
// Composite bloom back onto source
|
||||
ctx.commands.push({
|
||||
cmd: 'composite_textures',
|
||||
base: content_target,
|
||||
base: src_target,
|
||||
overlay: blur_src,
|
||||
output: output_target,
|
||||
mode: 'add'
|
||||
})
|
||||
|
||||
// Return a drawable that blits the result
|
||||
return {
|
||||
type: 'blit_target',
|
||||
layer: node.layer || 0,
|
||||
world_y: 0,
|
||||
target: output_target,
|
||||
width: target_size.width,
|
||||
height: target_size.height
|
||||
}
|
||||
return output_target
|
||||
}
|
||||
|
||||
// Render mask effect as an island
|
||||
function render_mask_island(node, camera, ctx, effect, parent_tint, parent_opacity, parent_scissor, parent_pos) {
|
||||
// Apply mask effect to a source target, returning a new target
|
||||
function apply_mask_effect(src_target, effect, ctx) {
|
||||
var backend = ctx.backend
|
||||
var target_size = ctx.target_size
|
||||
var camera = ctx.camera
|
||||
|
||||
// Allocate targets
|
||||
var content_target = backend.get_or_create_target(target_size.width, target_size.height, 'mask_content_' + ctx.target_counter++)
|
||||
var mask_target = backend.get_or_create_target(target_size.width, target_size.height, 'mask_source_' + ctx.target_counter++)
|
||||
var output_target = backend.get_or_create_target(target_size.width, target_size.height, 'mask_output_' + ctx.target_counter++)
|
||||
|
||||
// Collect children drawables (the content to be masked)
|
||||
var child_drawables = collect_children_drawables(node, camera, parent_tint, parent_opacity, parent_scissor, parent_pos, ctx)
|
||||
|
||||
// Render children to content target
|
||||
ctx.commands.push({cmd: 'begin_render', target: content_target, clear: {r: 0, g: 0, b: 0, a: 0}})
|
||||
ctx.commands.push({cmd: 'set_camera', camera: camera})
|
||||
|
||||
var batches = batch_drawables(child_drawables)
|
||||
for (var batch of batches) {
|
||||
if (batch.type == 'sprite_batch') {
|
||||
ctx.commands.push({
|
||||
cmd: 'draw_batch',
|
||||
batch_type: 'sprites',
|
||||
geometry: {sprites: batch.sprites},
|
||||
texture: batch.texture,
|
||||
material: batch.material
|
||||
})
|
||||
} else if (batch.type == 'text') {
|
||||
ctx.commands.push({cmd: 'draw_text', drawable: batch.drawable})
|
||||
}
|
||||
}
|
||||
ctx.commands.push({cmd: 'end_render'})
|
||||
|
||||
// Render mask source
|
||||
var mask_source = effect.source
|
||||
if (mask_source) {
|
||||
var mask_drawables = collect_drawables(mask_source, camera, null, null, null, null, ctx)
|
||||
|
||||
ctx.commands.push({cmd: 'begin_render', target: mask_target, clear: {r: 0, g: 0, b: 0, a: 0}})
|
||||
ctx.commands.push({cmd: 'set_camera', camera: camera})
|
||||
|
||||
var mask_batches = batch_drawables(mask_drawables)
|
||||
for (var batch of mask_batches) {
|
||||
if (batch.type == 'sprite_batch') {
|
||||
ctx.commands.push({
|
||||
cmd: 'draw_batch',
|
||||
batch_type: 'sprites',
|
||||
geometry: {sprites: batch.sprites},
|
||||
texture: batch.texture,
|
||||
material: batch.material
|
||||
})
|
||||
} else if (batch.type == 'text') {
|
||||
ctx.commands.push({cmd: 'draw_text', drawable: batch.drawable})
|
||||
}
|
||||
}
|
||||
ctx.commands.push({cmd: 'end_render'})
|
||||
render_drawables_to_target(mask_drawables, mask_target, camera, ctx)
|
||||
}
|
||||
|
||||
// Apply mask
|
||||
ctx.commands.push({
|
||||
cmd: 'apply_mask',
|
||||
content_texture: content_target,
|
||||
content_texture: src_target,
|
||||
mask_texture: mask_target,
|
||||
output: output_target,
|
||||
mode: effect.mode || 'alpha',
|
||||
invert: effect.invert || false
|
||||
})
|
||||
|
||||
// Return a drawable that blits the result
|
||||
return {
|
||||
type: 'blit_target',
|
||||
layer: node.layer || 0,
|
||||
world_y: 0,
|
||||
target: output_target,
|
||||
width: target_size.width,
|
||||
height: target_size.height
|
||||
}
|
||||
return output_target
|
||||
}
|
||||
|
||||
// Render blur effect as an island
|
||||
function render_blur_island(node, camera, ctx, effect, parent_tint, parent_opacity, parent_scissor, parent_pos) {
|
||||
// Apply blur effect to a source target, returning a new target
|
||||
function apply_blur_effect(src_target, effect, ctx) {
|
||||
var backend = ctx.backend
|
||||
var target_size = ctx.target_size
|
||||
|
||||
// Allocate targets
|
||||
var content_target = backend.get_or_create_target(target_size.width, target_size.height, 'blur_content_' + ctx.target_counter++)
|
||||
var blur_target_a = backend.get_or_create_target(target_size.width, target_size.height, 'blur_a_' + ctx.target_counter++)
|
||||
var blur_target_b = backend.get_or_create_target(target_size.width, target_size.height, 'blur_b_' + ctx.target_counter++)
|
||||
|
||||
// Collect children drawables
|
||||
var child_drawables = collect_children_drawables(node, camera, parent_tint, parent_opacity, parent_scissor, parent_pos, ctx)
|
||||
|
||||
// Render children to content target
|
||||
ctx.commands.push({cmd: 'begin_render', target: content_target, clear: {r: 0, g: 0, b: 0, a: 0}})
|
||||
ctx.commands.push({cmd: 'set_camera', camera: camera})
|
||||
|
||||
var batches = batch_drawables(child_drawables)
|
||||
for (var batch of batches) {
|
||||
if (batch.type == 'sprite_batch') {
|
||||
ctx.commands.push({
|
||||
cmd: 'draw_batch',
|
||||
batch_type: 'sprites',
|
||||
geometry: {sprites: batch.sprites},
|
||||
texture: batch.texture,
|
||||
material: batch.material
|
||||
})
|
||||
} else if (batch.type == 'text') {
|
||||
ctx.commands.push({cmd: 'draw_text', drawable: batch.drawable})
|
||||
}
|
||||
}
|
||||
ctx.commands.push({cmd: 'end_render'})
|
||||
|
||||
// Blur passes
|
||||
var blur_passes = effect.passes || 2
|
||||
var texel_size = [1/target_size.width, 1/target_size.height]
|
||||
var blur_src = content_target
|
||||
var blur_src = src_target
|
||||
var blur_dst = blur_target_a
|
||||
|
||||
for (var i = 0; i < blur_passes; i++) {
|
||||
@@ -424,15 +333,7 @@ function render_blur_island(node, camera, ctx, effect, parent_tint, parent_opaci
|
||||
blur_dst = tmp
|
||||
}
|
||||
|
||||
// Return a drawable that blits the result
|
||||
return {
|
||||
type: 'blit_target',
|
||||
layer: node.layer || 0,
|
||||
world_y: 0,
|
||||
target: blur_src,
|
||||
width: target_size.width,
|
||||
height: target_size.height
|
||||
}
|
||||
return blur_src
|
||||
}
|
||||
|
||||
// Collect drawables from children only (not the node itself)
|
||||
|
||||
Reference in New Issue
Block a user