kotlin
package com.example.wallpaper3d.shader
import android.content.Context
import android.opengl.GLES20
import android.opengl.GLSurfaceView
import android.service.wallpaper.WallpaperService
import android.util.Log
import android.view.SurfaceHolder
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
class ShaderWallpaperService : WallpaperService() {
override fun onCreateEngine(): Engine {
return ShaderEngine()
}
inner class ShaderEngine : Engine() {
private lateinit var glSurfaceView: GLSurfaceView
private lateinit var renderer: ShaderRenderer
override fun onCreate(surfaceHolder: SurfaceHolder) {
super.onCreate(surfaceHolder)
glSurfaceView = MyGLSurfaceView(this@ShaderWallpaperService)
renderer = ShaderRenderer(this@ShaderWallpaperService)
glSurfaceView.setRenderer(renderer)
glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
}
override fun onVisibilityChanged(visible: Boolean) {
super.onVisibilityChanged(visible)
if (visible) {
glSurfaceView.onResume()
} else {
glSurfaceView.onPause()
}
}
inner class MyGLSurfaceView(context: Context) : GLSurfaceView(context) {
init {
setEGLContextClientVersion(2)
holder.setFormat(android.graphics.PixelFormat.TRANSLUCENT)
setZOrderOnTop(false)
}
override fun getHolder(): SurfaceHolder {
return surfaceHolder // 使用壁纸引擎的 SurfaceHolder
}
}
}
}
class ShaderRenderer(val context: Context) : GLSurfaceView.Renderer {
// 顶点坐标(全屏四边形)
private val vertexCoords = floatArrayOf(
-1.0f, -1.0f, 0.0f, // 左下
1.0f, -1.0f, 0.0f, // 右下
-1.0f, 1.0f, 0.0f, // 左上
1.0f, 1.0f, 0.0f // 右上
)
private val vertexBuffer: FloatBuffer
// Shader 程序 ID
private var programId: Int = 0
// 时间和分辨率的 uniform 位置
private var timeLoc: Int = 0
private var resolutionLoc: Int = 0
private var startTime: Long = 0
init {
// 初始化顶点缓冲
vertexBuffer = ByteBuffer.allocateDirect(vertexCoords.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexCoords)
vertexBuffer.position(0)
}
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
// 编译并链接 shaders
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
val fragmentShaderCode = ShaderLoader.loadFromAssets(context,"shaders/shader_3.glsl")
val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
programId = GLES20.glCreateProgram().apply {
GLES20.glAttachShader(this, vertexShader)
GLES20.glAttachShader(this, fragmentShader)
GLES20.glLinkProgram(this)
}
GLES20.glUseProgram(programId)
// 获取 uniform 变量位置
timeLoc = GLES20.glGetUniformLocation(programId, "iTime")
resolutionLoc = GLES20.glGetUniformLocation(programId, "iResolution")
// 记录开始时间
startTime = System.currentTimeMillis()
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
// 传递分辨率给 shader
GLES20.glUniform2f(resolutionLoc, width.toFloat(), height.toFloat())
}
override fun onDrawFrame(gl: GL10?) {
GLES20.glClearColor(0f, 0f, 0f, 1f)
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
// 计算运行时间(秒)
val currentTime = (System.currentTimeMillis() - startTime) / 1000f
GLES20.glUniform1f(timeLoc, currentTime)
// 绑定顶点数据
val positionLoc = GLES20.glGetAttribLocation(programId, "aPosition")
GLES20.glEnableVertexAttribArray(positionLoc)
GLES20.glVertexAttribPointer(
positionLoc, 3, GLES20.GL_FLOAT, false,
3 * 4, vertexBuffer
)
// 绘制全屏四边形
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
GLES20.glDisableVertexAttribArray(positionLoc)
}
// 加载并编译 shader
private fun loadShader(type: Int, shaderCode: String): Int {
Log.i("ccccccc", "shader_code===$shaderCode")
return GLES20.glCreateShader(type).apply {
GLES20.glShaderSource(this, shaderCode)
GLES20.glCompileShader(this)
}
}
// 顶点着色器(全屏四边形)
private val vertexShaderCode = """
attribute vec3 aPosition;
void main() {
gl_Position = vec4(aPosition, 1.0);
}
""".trimIndent()
}
shader_3.glsl内容
ini
#version 100
precision mediump float;
uniform float iTime;
uniform vec2 iResolution;
// 旋转90度开关(注释掉可关闭旋转)
#define ROTATE_90
void main() {
vec2 fragCoord = gl_FragCoord.xy;
// 处理90度旋转:交换x/y坐标并修正方向
#ifdef ROTATE_90
// 交换x和y,实现旋转;反转x轴修正方向
fragCoord = vec2(fragCoord.y, iResolution.x - fragCoord.x);
// 旋转后重新计算分辨率比例(宽高互换)
vec2 r = vec2(iResolution.y, iResolution.x);
#else
vec2 r = iResolution.xy;
#endif
vec3 c;
float l, z = iTime;
// 减少迭代次数(原3次→2次,提升性能)
for(int i = 0; i < 2; i++) {
vec2 uv, p = fragCoord.xy / r;
uv = p;
// 坐标中心化并适配宽高比
p -= 0.5;
p.x *= r.x / r.y;
// 时间动画控制(略微降低速度,避免过度闪烁)
z += 0.05;
l = length(p);
// 核心扭曲计算(保留原效果)
uv += p / l * (sin(z) + 1.0) * abs(sin(l * 9.0 - z * 2.0));
// 颜色计算(增强对比度)
c[i] = 0.02 / length(mod(uv, 1.0) - 0.5);
}
// 补充第三通道颜色(保持三通道平衡)
c[2] = 0.02 / length(mod((fragCoord.xy / r) + sin(z)*0.1, 1.0) - 0.5);
// 输出颜色(调整透明度避免过亮)
gl_FragColor = vec4(c / l, 1.0);
}
拷贝对应的效果代码 扔给ai 然后替换glsl文件内容。这里仅限于 变量只有 iTime iResolution 的效果。如果有其他变量 需要ai帮你把其他变量的设置 补充上