如何在GPUImage中不按序列混合两个LUT的视频输出?
Absolutely, your desired functionality is totally achievable with GPUImage — nice catch pointing out GPUImageSoftEleganceFilter, since it uses a similar dual-LUT approach under the hood! Your current code chains two LUT filters in series (video → LUT1 → LUT2), but what you need is a parallel workflow:
- Grab a pixel color from the video
- Look up that color in both LUTs simultaneously to get two distinct output colors
- Mix those two colors
- Output the final blended color
Here's a clear, beginner-friendly implementation direction:
Core Concept: Custom Filter with Dual LUT Sampling
GPUImage's built-in filters don’t do this out of the box, but you can build a custom filter that handles parallel LUT lookups and mixing. The key is to reuse the LUT sampling logic from GPUImageLookupFilter and extend it to work with two LUT textures at once.
Step 1: Write a Custom Fragment Shader
This shader will take the video input, sample both LUTs using the same source color, then mix the results. Save this as DualLUTMix.fsh in your project:
varying highp vec2 textureCoordinate; uniform sampler2D inputImageTexture; uniform sampler2D lutTexture1; uniform sampler2D lutTexture2; uniform lowp float mixFactor; // Controls blend: 0.0 = LUT1 only, 1.0 = LUT2 only // Reuse GPUImage's standard LUT sampling logic highp vec4 sampleLUT(sampler2D lutTexture, highp vec3 color) { highp float blueColor = color.b * 63.0; highp vec2 quad1; quad1.y = floor(floor(blueColor) / 8.0); quad1.x = floor(blueColor) - (quad1.y * 8.0); highp vec2 quad2; quad2.y = floor(ceil(blueColor) / 8.0); quad2.x = ceil(blueColor) - (quad2.y * 8.0); highp vec2 texPos1; texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r); texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g); highp vec2 texPos2; texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.r); texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * color.g); lowp vec4 newColor1 = texture2D(lutTexture, texPos1); lowp vec4 newColor2 = texture2D(lutTexture, texPos2); return mix(newColor1, newColor2, fract(blueColor)); } void main() { // Get original video pixel color lowp vec4 sourceColor = texture2D(inputImageTexture, textureCoordinate); // Sample both LUTs with the same source color lowp vec4 lut1Result = sampleLUT(lutTexture1, sourceColor.rgb); lowp vec4 lut2Result = sampleLUT(lutTexture2, sourceColor.rgb); // Mix the two results (adjust this logic if you need a non-linear blend) gl_FragColor = mix(lut1Result, lut2Result, mixFactor); }
Step 2: Build a Custom GPUImageFilter Subclass
This class will manage the two LUT textures and expose controls for the mix factor. Create a DualLUTMixFilter class:
#import "GPUImageFilter.h" @interface DualLUTMixFilter : GPUImageFilter @property (nonatomic, assign) CGFloat mixFactor; // Range: 0.0 to 1.0 - (void)setLUTImage1:(UIImage *)lutImage; - (void)setLUTImage2:(UIImage *)lutImage; @end @implementation DualLUTMixFilter { GPUImagePicture *_lutPicture1; GPUImagePicture *_lutPicture2; GLint _lutTexture1Uniform; GLint _lutTexture2Uniform; GLint _mixFactorUniform; } - (instancetype)init { NSString *fragmentShader = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"DualLUTMix" ofType:@"fsh"] encoding:NSUTF8StringEncoding error:nil]; if (!(self = [super initWithFragmentShaderFromString:fragmentShader])) { return nil; } // Get references to shader uniforms _lutTexture1Uniform = [filterProgram uniformIndex:@"lutTexture1"]; _lutTexture2Uniform = [filterProgram uniformIndex:@"lutTexture2"]; _mixFactorUniform = [filterProgram uniformIndex:@"mixFactor"]; // Set default mix factor (50/50 blend) self.mixFactor = 0.5; return self; } - (void)setLUTImage1:(UIImage *)lutImage { if (_lutPicture1) [_lutPicture1 removeTarget:self]; _lutPicture1 = [[GPUImagePicture alloc] initWithImage:lutImage]; [_lutPicture1 addTarget:self atTextureLocation:1]; [_lutPicture1 processImage]; } - (void)setLUTImage2:(UIImage *)lutImage { if (_lutPicture2) [_lutPicture2 removeTarget:self]; _lutPicture2 = [[GPUImagePicture alloc] initWithImage:lutImage]; [_lutPicture2 addTarget:self atTextureLocation:2]; [_lutPicture2 processImage]; } - (void)setMixFactor:(CGFloat)mixFactor { _mixFactor = clamp(mixFactor, 0.0, 1.0); [self setFloat:_mixFactor forUniform:_mixFactorUniform program:filterProgram]; } @end
Step 3: Integrate the Filter into Your Workflow
Replace your existing chained LUT filters with this custom filter:
// Initialize your video player and views as before playerItem = [[AVPlayerItem alloc] initWithURL:fileURL]; player = [[AVPlayer alloc] initWithPlayerItem:playerItem]; // Initialize custom dual LUT filter DualLUTMixFilter *dualLUTFilter = [[DualLUTMixFilter alloc] init]; [dualLUTFilter setLUTImage1:lutImage1]; [dualLUTFilter setLUTImage2:lutImage2]; dualLUTFilter.mixFactor = 0.7; // Adjust blend ratio as needed // Connect the processing pipeline GPUImageMovie *gpuImageMovie = [[GPUImageMovie alloc] initWithPlayerItem:playerItem]; [gpuImageMovie addTarget:dualLUTFilter]; [dualLUTFilter addTarget:gpuImageView]; // Start playback and processing [gpuImageMovie startProcessing]; [player play];
Key Notes for Beginners
- The
sampleLUTfunction is identical to whatGPUImageLookupFilteruses — you don’t need to reinvent this logic. - If you need a more complex blend than linear mixing (e.g., based on luminance or hue), modify the
gl_FragColorline in the shader to use custom blending math. - GPUImageSoftEleganceFilter works exactly this way: it samples two LUTs and blends their results to create its "soft elegance" effect, so you can peek at its source code for additional inspiration.
内容的提问来源于stack exchange,提问作者timrichardsn

