「バブルの中に入れたい」画像がいくつかあります。これらの画像が中に閉じ込められた状態で、泡が画面の周りに浮かんでいます。
内側の画像と泡の画像を組み合わせて、内側の画像を何らかの形で変形させて、泡の内側に映るように見せる方法が最適です。
テクスチャとメッシュを使用せずにこの効果を実現する方法を知っている人はいますか? おそらく誰かが古いプロジェクトや似たようなことをした何かを覚えていますか?
これが私が意味することの例です:
「バブルの中に入れたい」画像がいくつかあります。これらの画像が中に閉じ込められた状態で、泡が画面の周りに浮かんでいます。
内側の画像と泡の画像を組み合わせて、内側の画像を何らかの形で変形させて、泡の内側に映るように見せる方法が最適です。
テクスチャとメッシュを使用せずにこの効果を実現する方法を知っている人はいますか? おそらく誰かが古いプロジェクトや似たようなことをした何かを覚えていますか?
これが私が意味することの例です:
これは、私のオープン ソースGPUImageフレームワークの GPUImageSphereRefractionFilter を使用して行うことができます。
これがどのように機能するかについては、Android での同様の影響に関する質問への回答で詳しく説明しています。基本的に、フラグメント シェーダーを使用して架空の球体を通過する光を屈折させ、それを使用してソース イメージを含むテクスチャをルックアップします。背景は単純なガウスぼかしを使用してぼかします。
表示されている画像の正確な外観を実現したい場合は、このフラグメント シェーダーを微調整して、球体にグレージング アングル カラーを追加する必要があるかもしれませんが、これでかなり近くなるはずです。
おもしろいので、上のガラス球をもっと忠実に再現してみることにしました。グレージング アングル ライティングとスペキュラー ライティングの反射を球体に追加し、屈折したテクスチャ座標を反転させないことで、次の結果が得られました。
この新しいバージョンでは、次のフラグメント シェーダーを使用しました。
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp vec2 center;
uniform highp float radius;
uniform highp float aspectRatio;
uniform highp float refractiveIndex;
// uniform vec3 lightPosition;
const highp vec3 lightPosition = vec3(-0.5, 0.5, 1.0);
const highp vec3 ambientLightPosition = vec3(0.0, 0.0, 1.0);
void main()
{
highp vec2 textureCoordinateToUse = vec2(textureCoordinate.x, (textureCoordinate.y * aspectRatio + 0.5 - 0.5 * aspectRatio));
highp float distanceFromCenter = distance(center, textureCoordinateToUse);
lowp float checkForPresenceWithinSphere = step(distanceFromCenter, radius);
distanceFromCenter = distanceFromCenter / radius;
highp float normalizedDepth = radius * sqrt(1.0 - distanceFromCenter * distanceFromCenter);
highp vec3 sphereNormal = normalize(vec3(textureCoordinateToUse - center, normalizedDepth));
highp vec3 refractedVector = 2.0 * refract(vec3(0.0, 0.0, -1.0), sphereNormal, refractiveIndex);
refractedVector.xy = -refractedVector.xy;
highp vec3 finalSphereColor = texture2D(inputImageTexture, (refractedVector.xy + 1.0) * 0.5).rgb;
// Grazing angle lighting
highp float lightingIntensity = 2.5 * (1.0 - pow(clamp(dot(ambientLightPosition, sphereNormal), 0.0, 1.0), 0.25));
finalSphereColor += lightingIntensity;
// Specular lighting
lightingIntensity = clamp(dot(normalize(lightPosition), sphereNormal), 0.0, 1.0);
lightingIntensity = pow(lightingIntensity, 15.0);
finalSphereColor += vec3(0.8, 0.8, 0.8) * lightingIntensity;
gl_FragColor = vec4(finalSphereColor, 1.0) * checkForPresenceWithinSphere;
}
このフィルターは、GPUImageGlassSphereFilter を使用して実行できます。
記録のために、@BradLarsonが提案したように GPUImage を使用することになりましたが、以下のようにカスタムフィルターを作成する必要がありました。このフィルタは、「内側」の画像と泡のテクスチャを取得し、2 つをブレンドしながら、屈折計算も実行しますが、画像座標を反転しません。の効果:
.h
@interface GPUImageBubbleFilter : GPUImageTwoInputFilter
@property (readwrite, nonatomic) CGFloat refractiveIndex;
@property (readwrite, nonatomic) CGFloat radius;
@end
.m
#import "GPUImageBubbleFilter.h"
NSString *const kGPUImageBubbleShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
varying highp vec2 textureCoordinate2;
uniform sampler2D inputImageTexture;
uniform sampler2D inputImageTexture2;
uniform highp vec2 center;
uniform highp float radius;
uniform highp float aspectRatio;
uniform highp float refractiveIndex;
void main()
{
highp vec2 textureCoordinateToUse = vec2(textureCoordinate.x, (textureCoordinate.y * aspectRatio + 0.5 - 0.5 * aspectRatio));
highp float distanceFromCenter = distance(center, textureCoordinateToUse);
lowp float checkForPresenceWithinSphere = step(distanceFromCenter, radius);
distanceFromCenter = distanceFromCenter / radius;
highp float normalizedDepth = radius * sqrt(1.0 - distanceFromCenter * distanceFromCenter);
highp vec3 sphereNormal = normalize(vec3(textureCoordinateToUse - center, normalizedDepth));
highp vec3 refractedVector = refract(vec3(0.0, 0.0, -1.0), sphereNormal, refractiveIndex);
lowp vec4 textureColor = texture2D(inputImageTexture, (refractedVector.xy + 1.0) * 0.5) * checkForPresenceWithinSphere;
lowp vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2) * checkForPresenceWithinSphere;
gl_FragColor = mix(textureColor, textureColor2, textureColor2.a);
}
);
@interface GPUImageBubbleFilter () {
GLint radiusUniform, centerUniform, aspectRatioUniform, refractiveIndexUniform;
}
@property (readwrite, nonatomic) CGFloat aspectRatio;
@end
@implementation GPUImageBubbleFilter
@synthesize radius = _radius, refractiveIndex = _refractiveIndex, aspectRatio = _aspectRatio;
- (id) init {
self = [super initWithFragmentShaderFromString: kGPUImageBubbleShaderString];
if( self ) {
radiusUniform = [filterProgram uniformIndex: @"radius"];
aspectRatioUniform = [filterProgram uniformIndex: @"aspectRatio"];
centerUniform = [filterProgram uniformIndex: @"center"];
refractiveIndexUniform = [filterProgram uniformIndex: @"refractiveIndex"];
self.radius = 0.5;
self.refractiveIndex = 0.5;
self.aspectRatio = 1.0;
GLfloat center[2] = {0.5, 0.5};
[GPUImageOpenGLESContext useImageProcessingContext];
[filterProgram use];
glUniform2fv(centerUniform, 1, center);
[self setBackgroundColorRed: 0 green: 0 blue: 0 alpha: 0];
}
return self;
}
#pragma mark - Accessors
- (void) setRadius:(CGFloat)radius {
_radius = radius;
[GPUImageOpenGLESContext useImageProcessingContext];
[filterProgram use];
glUniform1f(radiusUniform, _radius);
}
- (void) setAspectRatio:(CGFloat)aspectRatio {
_aspectRatio = aspectRatio;
[GPUImageOpenGLESContext useImageProcessingContext];
[filterProgram use];
glUniform1f(aspectRatioUniform, _aspectRatio);
}
- (void)setRefractiveIndex:(CGFloat)newValue;
{
_refractiveIndex = newValue;
[GPUImageOpenGLESContext useImageProcessingContext];
[filterProgram use];
glUniform1f(refractiveIndexUniform, _refractiveIndex);
}