-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path_ff_cgpt_normal_flowmap_transcript_1.lua
418 lines (292 loc) · 27.3 KB
/
_ff_cgpt_normal_flowmap_transcript_1.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
•• One of the things I'm trying to figure out for use in Filter Forge is the creation of flow maps (the type used in 3d) from height data. Is that something you could help me with?
Could you give me a bit more information on what you are trying to accomplish? Are you trying to create a flow map from a 2D height map that can be used for water or other fluid simulations in a 3D application?
•• That would be one application, but I'm also interested in using flow maps with Lookup, Offset or Rotation components to allow for texture mapping that follows the flow.
Got it, in that case, I can give you an overview of the general approach for generating flow maps from height maps, and then you can adapt it to your specific needs in Filter Forge.
Flow maps are 2D textures that encode the direction of a fluid's flow over a surface. In the context of texture mapping, they can be used to create the illusion of movement or deformation on a surface by using them to distort a texture.
The general approach for generating a flow map from a height map is as follows:
1
Compute the surface gradient at each pixel in the height map. The surface gradient is a 2D vector that points in the direction of the steepest ascent at that point. This can be approximated by computing the differences between adjacent pixels in the height map and taking the cross-product of the resulting vectors.
2
Normalize the surface gradients to ensure that they have a unit length. This is necessary because flow maps encode directions, not magnitudes.
3
Encode the surface gradient vectors as RGB colors. This is done by mapping the x, y, and z components of each vector to the red, green, and blue channels of a pixel, respectively. For example, if the surface gradient vector is (0.5, 0.5, 0), then the corresponding pixel in the flow map would be (R:0.5, G:0.5, B:0).
4
Encode the sign of the dot product between the surface gradient and the surface normal as the alpha channel of each pixel. This is done to indicate whether the surface is sloping up or down at that point. If the dot product is positive, then the surface is sloping up and the alpha value should be 1.0. If the dot product is negative, then the surface is sloping down and the alpha value should be 0.0.
Once you have generated a flow map from a height map, you can use it to distort a texture by using it to look up texels in the texture based on the direction and magnitude of the flow at each pixel.
In Filter Forge, you can implement this approach using a combination of the built-in filters and custom scripts. For example, you could use the Gradient filter to compute the surface gradients, and the Normalize filter to normalize them. Then you could use a script to encode the surface gradients and surface normals as colors and generate the flow map. Finally, you could use the Lookup, Offset, or Rotation filters to apply the flow map to a texture.
•• Let's do a lua script for a Filter Forge map script for generating flow maps from height maps. We can follow your breakdown:
Sure, here's an example Lua script for a Filter Forge map script that generates a flow map from a height map:
-- flow map prototype
function prepare()
-- get inputs and set up
height_source = get_source_image(HM)
normal_source = get_source_image(NM)
height_scale = get_slider_input(SCALE)
end
function get_sample(x, y)
-- get height and normal data
local height = get_sample_map(x, y, height_source) * height_scale
local normal_x, normal_y, normal_z = get_sample_map_normal(x, y, normal_source)
-- compute surface gradient and normalize
local gradient_x = get_sample_map(x + 1, y, height_source) - get_sample_map(x - 1, y, height_source)
local gradient_y = get_sample_map(x, y + 1, height_source) - get_sample_map(x, y - 1, height_source)
local gradient_z = 2.0 / height_scale
local gradient_length = math.sqrt(gradient_x^2 + gradient_y^2 + gradient_z^2)
gradient_x = gradient_x / gradient_length
gradient_y = gradient_y / gradient_length
gradient_z = gradient_z / gradient_length
-- compute dot product of normal and gradient to determine slope direction
local dot_product = normal_x * gradient_x + normal_y * gradient_y + normal_z * gradient_z
local alpha = dot_product > 0.0 and 1.0 or 0.0
-- encode gradient as RGB and slope direction as alpha
local r = (gradient_x + 1.0) / 2.0
local g = (gradient_y + 1.0) / 2.0
local b = (gradient_z + 1.0) / 2.0
return r, g, b, alpha
end
This script assumes that the height map and normal map have been provided as source images with the IDs "HM" and "NM", respectively, and that a slider input with ID "SCALE" has been set up to control the vertical scale of the height map. `height_scale` represents the amount of height change per unit of texture space. In other words, it determines how much a change in the height map affects the flow map. A larger value means that small variations in the height map will have a larger effect on the flow map, while a smaller value means that only large variations in the height map will affect the flow map.
For example, if height_scale is set to 0.1, then a height difference of 1 unit in the height map will produce a flow vector with a length of 0.1 units in the flow map.
The get_sample function is called for each pixel in the output flow map and computes the surface gradient at that pixel using the differences between adjacent pixels in the height map. The resulting gradient is normalized to ensure that it has a unit length, and then encoded as an RGB color by mapping the x, y, and z components of the gradient to the red, green, and blue channels of the output pixel. The sign of the dot product between the gradient and the surface normal is encoded as the alpha channel of the output pixel, with a value of 1.0 indicating that the surface is sloping up and 0.0 indicating that it is sloping down.
•• Excellent!
•• I have not seen the `get_source_image` function, so I'd like to know more about it. I'm also curious about the way you used `get_sample_map` to compute the surface gradient.
Sure! get_source_image is a Filter Forge Lua function that returns the input image that is being processed by the current map script. You can use this function to access the input image's properties, such as its dimensions, or to retrieve pixel samples from it.
As for get_sample_map, it is another Filter Forge Lua function that returns the color values of a given pixel in the input image. In the script I provided, I used this function to calculate the surface gradient by computing the differences between the adjacent pixels in the height map and taking the cross-product of the resulting vectors. Here's the relevant code:
local dx = vec3(get_sample_map(x + 1, y, SOURCE)) - vec3(get_sample_map(x - 1, y, SOURCE))
local dy = vec3(get_sample_map(x, y + 1, SOURCE)) - vec3(get_sample_map(x, y - 1, SOURCE))
local gradient = dx:cross(dy)
Here, dx and dy represent the surface gradients in the x and y directions, respectively. We calculate them by subtracting the color values of the adjacent pixels in the height map and converting them to vectors using the vec3 function. We then take the cross-product of these vectors to obtain the surface gradient vector, which represents the steepest ascent at the current pixel.
I am used to setting up inputs for `get_sample_map` and `get_sample_grayscale` but I don't recall seeing a way to create an input for `get_source_image` using the Input Editor.
get_source_image is not an input function that can be defined in the Filter Forge Input Editor. Instead, it is a built-in function in the Filter Forge script API that retrieves the source image of the current filter node.
When writing a Filter Forge script, you can use get_source_image function to retrieve the source image in the form of a 2D array of pixel values. Here's an example of how you can use get_source_image in a script:
function main()
-- get the source image
local source_image = get_source_image()
-- process the source image here
-- return the processed image
return source_image
end
Regarding the use of get_sample_map to compute the surface gradient, get_sample_map is used here to retrieve the height values of neighboring pixels in the height map. By computing the differences between adjacent pixels in the height map and taking the cross-product of the resulting vectors, we can obtain the surface gradient vector at each pixel.
Here's an example of how to compute the surface gradient vector at a given pixel (x, y) in a height map:
-- Get the height values of neighboring pixels
local height_tl = get_sample_map(x - 1, y - 1, SOURCE)
local height_t = get_sample_map(x, y - 1, SOURCE)
local height_tr = get_sample_map(x + 1, y - 1, SOURCE)
local height_l = get_sample_map(x - 1, y, SOURCE)
local height_r = get_sample_map(x + 1, y, SOURCE)
local height_bl = get_sample_map(x - 1, y + 1, SOURCE)
local height_b = get_sample_map(x, y + 1, SOURCE)
local height_br = get_sample_map(x + 1, y + 1, SOURCE)
-- Compute the surface gradient vector
local dx = (height_tr + 2 * height_r + height_br) - (height_tl + 2 * height_l + height_bl)
local dy = (height_bl + 2 * height_b + height_br) - (height_tl + 2 * height_t + height_tr)
local dz = 1.0 / 8.0 -- This is the size of a pixel in Filter Forge
local surface_gradient = normalize(vec3(dx, dy, dz))
In this example, normalize is a function that normalizes a 3D vector to have unit length. vec3 is a function that constructs a 3D vector from its three components. Note that the dz value is set to 1.0 / 8.0 because that is the size of a pixel in Filter Forge. This ensures that the resulting surface gradient vector has unit length.
•• Okay, I'm not clear on what the source image of the current filter node is, or what qualifies as `the current filter node`. Also, you define two source images; I can provide a height and normal, but how do I do so?
In the context of Filter Forge, the "source image" of the current filter node refers to the image that is being processed by the current filter node in the Filter Forge graph. When you create a new filter node in the graph, you can define its inputs and outputs, and the input image for the current filter node is automatically set to be the output image of the preceding filter node in the graph.
To provide a height and normal map as inputs to your filter, you can add two input ports to your Filter Forge node and define them as images in the Input Editor. Then, in the Filter Forge graph, connect the output of the height map input port to the input of your filter node, and do the same for the normal map input. This way, when your filter is executed, it will receive the height and normal maps as inputs through the input ports.
Here's an example of how you can define two input ports for height and normal maps in a Filter Forge node:
function setup()
-- define input ports for height and normal maps
define_input_port("HeightMap", "Image", {})
define_input_port("NormalMap", "Image", {})
end
Then, in the Filter Forge graph, you can connect the output of a height map generator node to the "HeightMap" input port of your filter node, and connect the output of a normal map generator node to the "NormalMap" input port of your filter node.
Regarding the use of get_source_image in the flow map script, it was used to get the height and normal maps from the input ports that were defined in the Filter Forge node. If you define input ports for your height and normal maps, you can replace the calls to get_source_image with get_input_image and pass the names of your input ports as arguments to these functions. For example:
function prepare()
-- get height and normal maps from input ports
height_map = get_input_image("HeightMap")
normal_map = get_input_image("NormalMap")
end
Then, you can use the height_map and normal_map variables in your code to compute the surface gradient and encode it as a flow map.
•• Okay. So, the filter node in this case is the map script component?
Yes, that's correct. In this case, the current filter node would be the map script component that contains the code for generating the flow map.
To provide the height and normal maps as inputs to the map script component, you would need to add two input nodes to the filter network and connect them to the map script component.
To do this, click on the "Add Input Node" button in the toolbar, and select "Image" for the type of input. Then, you can drag and drop your height map and normal map images into the input nodes to load them into Filter Forge.
Once you've added the input nodes and connected them to the map script component, you can reference them in the script using the get_source_image() function, passing the ID of the input node as an argument.
•• Earlier, we discussed using a method known as "sobel filtering" to compute the surface normals. You explained that Sobel filtering involves taking the gradient of an image in both the x and y directions using a kernel that is designed to highlight edges in the image. The resulting x and y gradients can then be used to compute the surface normal at each pixel.
Here is script I'd like updated to an implementation of sobel filtering in Lua:
-- normal
function prepare()
-- get inputs and set up
height_scale = get_slider_input(SCALE)
-- most textures are 1:1. if another aspect ratio is used, the shorter axis will be [0, 1] and the other will exceed 1.
-- to preserve pixel aspect ratio, use the min axis.
offset = 1 / math.min(OUTPUT_HEIGHT, OUTPUT_WIDTH)
end
function get_sample(x, y)
-- get height and surface gradient
local height = get_sample_grayscale(x, y, HEIGHT) * height_scale
local gradient_x = (get_sample_grayscale(x + offset, y, HEIGHT) - get_sample_grayscale(x - offset, y, HEIGHT)) / (2 * offset)
local gradient_y = (get_sample_grayscale(x, y + offset, HEIGHT) - get_sample_grayscale(x, y - offset, HEIGHT)) / (2 * offset)
local gradient_z = 1 / math.sqrt(1 + gradient_x^2 + gradient_y^2)
-- compute normal vector
local nx = gradient_x * gradient_z
local ny = gradient_y * gradient_z
local nz = 1 - gradient_z
-- encode normal vector as RGB
local r = (nx + 1.0) / 2.0
local g = (ny + 1.0) / 2.0
local b = ((nz + 1.0) / 2.0) * 0.5 + 0.5
local alpha = 1.0
return r, g, b, alpha
end
Sure, here's an implementation of Sobel filtering for computing surface normals in Lua:
-- sobel filtering for surface normals
function prepare()
-- get inputs and set up
height_scale = get_slider_input(SCALE)
-- most textures are 1:1. if another aspect ratio is used, the shorter axis will be [0, 1] and the other will exceed 1.
-- to preserve pixel aspect ratio, use the min axis.
offset = 1 / math.min(OUTPUT_HEIGHT, OUTPUT_WIDTH)
end
function get_sample(x, y)
-- get height and surface gradient
local height = get_sample_grayscale(x, y, HEIGHT) * height_scale
-- sobel kernel
local kernel_x = {
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1}
}
local kernel_y = {
{-1, -2, -1},
{0, 0, 0},
{1, 2, 1}
}
-- compute gradients
local gx = 0
local gy = 0
for ky = -1, 1 do
for kx = -1, 1 do
local weight = 1
if ky == 0 and kx == 0 then
weight = 2
end
local height_sample = get_sample_grayscale(x + kx * offset, y + ky * offset, HEIGHT)
local gradient_x = kernel_x[ky+2][kx+2] * height_sample
local gradient_y = kernel_y[ky+2][kx+2] * height_sample
gx = gx + gradient_x * weight
gy = gy + gradient_y * weight
end
end
local gradient_z = 1 / math.sqrt(1 + gx^2 + gy^2)
-- compute normal vector
local nx = gx * gradient_z
local ny = gy * gradient_z
local nz = 1 - gradient_z
-- encode normal vector as RGB
local r = (nx + 1.0) / 2.0
local g = (ny + 1.0) / 2.0
local b = ((nz + 1.0) / 2.0) * 0.5 + 0.5
local alpha = 1.0
return r, g, b, alpha
end
This code defines two Sobel kernels, kernel_x and kernel_y, which are used to compute the x and y gradients of the input image, respectively. The gradients are computed using the kernel by looping over the neighboring pixels, multiplying each pixel by the corresponding weight in the kernel, and summing the results.
After computing the gradients, the code computes the surface normal as before, using the x and y gradients to compute the x and y components of the normal and the z gradient to compute the z component. Finally, the normal is encoded as an RGB color and returned.
•• I was curious about the inner workings of the RGB Math Derivative component in Filter Forge. How would I implement it as a Map Script (in Lua)?
To implement the RGB Math Derivative component as a Map Script in Lua, you'll need to understand the underlying calculations involved. Here's a general overview of the steps you can take to implement the RGB Math Derivative component:
1. Retrieve the RGB input values: In your get_sample() function, use the get_sample_map() function to retrieve the RGB values at the current pixel coordinates (x, y).
2. Calculate the derivatives: The RGB Math Derivative component typically calculates the derivatives of the RGB channels with respect to the X and Y coordinates. To calculate the derivatives, you can use numerical differentiation techniques, such as finite differences.
• For the X derivative, you can calculate it as delta_r_x = r(x + 1, y) - r(x - 1, y), delta_g_x = g(x + 1, y) - g(x - 1, y), and delta_b_x = b(x + 1, y) - b(x - 1, y).
• For the Y derivative, you can calculate it as delta_r_y = r(x, y + 1) - r(x, y - 1), delta_g_y = g(x, y + 1) - g(x, y - 1), and delta_b_y = b(x, y + 1) - b(x, y - 1).
Note: Make sure to handle the edge cases appropriately (e.g., when x - 1 or y - 1 is out of bounds).
3. Normalize the derivatives: Depending on your specific requirements, you might need to normalize the derivatives to a specific range or scale. This step ensures that the values are within the desired range for further processing or visualization.
4. Use the derivatives in your calculations: Once you have the derivatives, you can use them in your calculations as needed. This might involve combining them with other variables, applying mathematical operations, or using them to modify the RGB values or other parameters.
5. Return the modified RGB values: Finally, return the modified RGB values from the get_sample() function to update the pixel colors in the output image.
It's important to note that the exact implementation details may vary depending on the specific requirements of the RGB Math Derivative component and the capabilities of the Filter Forge software. The steps outlined above provide a general framework to get you started, but you may need to adapt and refine them based on your specific use case.
Additionally, referring to the Filter Forge documentation or resources specific to the RGB Math Derivative component can provide more detailed information on the exact calculations and any additional considerations required.
If you have access to specific documentation or further details about the RGB Math Derivative component, I can provide more specific guidance tailored to your requirements.
•• Here is what's mentioned in the Help documents:
The Derivative component calculates the channel-wise directional derivative of a surface specified by its Source input along the direction specified by its Angle input. To produce one output sample, Derivative takes two samples of its Source input. The samples are located on a line passing through the original sample point to the right and to the left of it (the distance between these two samples is very small). Each output color channel is calculated as a difference between the two sampled channel values divided by the distance between the sample points. The resulting alpha channel is calculated by averaging the alpha channel values of the two samples.
Derivative
Derivative can be used for detecting edges, simulating snow or dust-covered surfaces, assembling custom normal maps, or creating advanced lighting effects.
Derivative is a map component, it can be located in the RGB Math category on the Components Bar. This component can output HDR colors. Like other RGB Math components, this component processes its inputs on a channel-wise basis, treating RGB channel values as signed floating-point numbers. For more information, see How Math Components Work.
Source: Map Input (HDR), Required
Provides the channel-wise surface height. Derivative treats the input image as three height maps (one for each of the RGB channels) and calculates the surface slope for a specified "viewing angle" – the steeper the slope, the brighter the output. Source is a required input – in order to make Derivative work, this input must be connected. This input can accept HDR colors.
Angle: Map Input (HDR)
Specifies the angle for calculating the derivative. This input can accept HDR colors.
Based on the information provided in the Help documents, here's how you can implement the RGB Math Derivative component as a Map Script in Lua:
-- Variables to store the sampled values
local r_left, r_right, g_left, g_right, b_left, b_right
local x_delta, y_delta -- Distance between sample points
function get_sample(x, y)
-- Sample the Source input at the current pixel coordinates and its neighbors
r_left, g_left, b_left, _ = get_sample_map(x - 1, y, SOURCE)
r_right, g_right, b_right, _ = get_sample_map(x + 1, y, SOURCE)
-- Calculate the differences between the sampled channel values
local delta_r = r_right - r_left
local delta_g = g_right - g_left
local delta_b = b_right - b_left
-- Calculate the distance between the sample points
x_delta = 2 -- Assuming the distance between the sample points is 2 pixels
-- Normalize the derivatives by dividing them by the distance
local derivative_r = delta_r / x_delta
local derivative_g = delta_g / x_delta
local derivative_b = delta_b / x_delta
-- Return the modified RGB values as the output
return derivative_r, derivative_g, derivative_b, 1
end
Please note that in the provided code, the assumptions are made based on the information provided in the Help documents. Adjustments might be necessary to match the actual behavior of the RGB Math Derivative component in Filter Forge. Additionally, you may need to incorporate further logic or modifications depending on your specific requirements or additional information about the component's behavior.
It's always recommended to refer to the official Filter Forge documentation or consult the resources specific to the RGB Math Derivative component for more accurate implementation details and any additional considerations.
•• In FF, the sample coordinates x and y are bounded to 0, 1 (divided by the output resolution) so the increment (-/+) and distance `x_delta` would need to be based on a variable derived from the image width and height.
Apologies for the oversight. You're correct that the sample coordinates are bounded to the range [0, 1], and the increment and distance should be adjusted accordingly based on the image width and height. Here's an updated version of the code that takes into account the image dimensions:
-- Variables to store the sampled values
local r_left, r_right, g_left, g_right, b_left, b_right
local x_delta, y_delta -- Distance between sample points
function prepare()
-- Get the width and height of the output image
local width, height = get_output_resolution()
-- Calculate the distance between sample points based on image dimensions
x_delta = 2 / width -- Assuming the distance between sample points is 2 pixels
-- Calculate the distance between sample points along the y-axis
y_delta = 0 -- No vertical sampling in this case
end
function get_sample(x, y)
-- Sample the Source input at the current pixel coordinates and its neighbors
r_left, g_left, b_left, _ = get_sample_map(x - x_delta, y - y_delta, SOURCE)
r_right, g_right, b_right, _ = get_sample_map(x + x_delta, y + y_delta, SOURCE)
-- Calculate the differences between the sampled channel values
local delta_r = r_right - r_left
local delta_g = g_right - g_left
local delta_b = b_right - b_left
-- Normalize the derivatives by dividing them by the distance
local derivative_r = delta_r / x_delta
local derivative_g = delta_g / x_delta
local derivative_b = delta_b / x_delta
-- Return the modified RGB values as the output
return derivative_r, derivative_g, derivative_b, 1
end
In the prepare function, we calculate the x_delta value based on the width of the output image. Then, in the get_sample function, we adjust the sample coordinates by multiplying x_delta and y_delta with the respective increments. This ensures that the sampling points are correctly aligned within the [0, 1] range.
Please note that the code assumes no vertical sampling (y_delta = 0). If vertical sampling is required, you can adjust the y_delta value accordingly.
Make sure to test and validate the code with your specific requirements and verify that it produces the desired results in the Filter Forge environment.
•• In FF, "the Derivative component calculates the channel-wise directional derivative of a surface specified by its Source input along the direction specified by its Angle input." So, I assume the x and y both are used: "To produce one output sample, Derivative takes two samples of its Source input. The samples are located on a line passing through the original sample point to the right and to the left of it." Technically, it's a vector calculation, yes?
Apologies for the confusion. You are correct that the Derivative component in Filter Forge calculates the directional derivative of a surface along the specified angle. To achieve this, the component considers both the x and y coordinates in a vector calculation.
Based on that information, here's an updated version of the code that incorporates the vector calculation using both x and y coordinates:
-- Variables to store the sampled values
local r_left, r_right, g_left, g_right, b_left, b_right
local x_delta, y_delta -- Distance between sample points
function prepare()
-- Get the width and height of the output image
local width, height = get_output_resolution()
-- Calculate the distance between sample points based on image dimensions
x_delta = 2 / width -- Assuming the distance between sample points is 2 pixels
y_delta = 2 / height -- Assuming the distance between sample points is 2 pixels
end
function get_sample(x, y)
-- Sample the Source input at the current pixel coordinates and its neighbors
r_left, g_left, b_left, _ = get_sample_map(x - x_delta, y - y_delta, SOURCE)
r_right, g_right, b_right, _ = get_sample_map(x + x_delta, y + y_delta, SOURCE)
-- Calculate the differences between the sampled channel values
local delta_r = r_right - r_left
local delta_g = g_right - g_left
local delta_b = b_right - b_left
-- Normalize the derivatives by dividing them by the distance
local derivative_r = delta_r / x_delta
local derivative_g = delta_g / y_delta
local derivative_b = delta_b / x_delta
-- Return the modified RGB values as the output
return derivative_r, derivative_g, derivative_b, 1
end
In this updated version, both x_delta and y_delta are calculated based on the width and height of the output image. The sampling points are adjusted by multiplying x_delta and y_delta with the respective increments. This allows the Derivative component to consider both the horizontal and vertical directions when calculating the derivatives.
Again, please test and validate the code with your specific requirements and verify that it produces the desired results in the Filter Forge environment.