MC还原广州塔灯光的设想、截至目前的所有尝试

2025-09-07_22.41.47
这是25年月7日在MC里还原的广州塔灯光效果

我在2023年下半年在游戏中用小木斧搭建了一个2:1的广州塔,当时搭建出来后,到了夜晚少了广州塔土土的灯光就觉得没有那味,所以就开始尝试各种方法去还原灯光效果

尝试一:使用材质包的动画效果(2024年8月实现)

想必这应该是最简单粗暴的实现效果了,想要达到既能变化颜色又能在夜晚发光的效果,使用支持optifine的游戏版本和制作一个支持emissive材质包就行了

2024-08-16_23.28.00

参考文章:
https://ewanhowell.com/guides/emissive-textures/
https://optifine.readthedocs.io/emissive_textures.html
https://optifine.readthedocs.io/custom_animations.html

参考支持emissive的材质包:
https://ewanhowell.com/resourcepacks/example-emissive-textures/

第一步:将一个现成的材质包删减成只剩以下目录结构

├─pack.mcmeta
├─pack.png
└─assets
    ├─其他模组材质目录
    │  ├─optifine
    │  └─textures
    │      └─block
    └─minecraft
        ├─optifine
        └─textures
            └─block

第二步:在 assets/minecraft/optifine 创建一个 emissive.properties 文件,保存以下内容

suffix.emissive=_e

此.properties文件会使optifine将材质包中名称以 _e 结尾的任何纹理加载为自发光纹理

第三步:创建动画纹理

  1. 选择16种不同方块(建议选用染色方块,刚好16种),我用的是1.20.1版本的coloredbrick模组的染色方块
  2. 类似水的材质,材质动画要求将所有动画帧画在同一个png图片中,垂直或竖直排列,为了复原广州塔灯效,则需要借助绘图工具绘制和导出,例如即时设计,可直接导出像素png并直接命名材质名,注意添加后缀 _e
  3. 将材质图片放入 assets\minecraft(或其他模组名)\textures\block 里面,并用python执行以下代码:
点击查看python代码
import os
import json

def create_mcmeta_files(directory):
    # 遍历目录中的所有文件
    for filename in os.listdir(directory):
        # 检查文件名是否以'_e.png'结尾
        if filename.endswith('_e.png'):
            # 构建.mcmeta文件名
            mcmeta_filename = filename + '.mcmeta'
            mcmeta_path = os.path.join(directory, mcmeta_filename)
            
            # 创建要写入的JSON数据
            mcmeta_data = {
                "ctm": {
                    "ctm_version": 1,
                    "layer": "CUTOUT",
                    "extra": {
                        "light": 12
                    }
                },
                "animation": {
                    "width": 1,
                    "height": 1,
                    "frametime": 4
                }
            }
            
            # 将JSON数据写入文件
            with open(mcmeta_path, 'w') as f:
                json.dump(mcmeta_data, f, indent=4)
            
            print(f"已创建: {mcmeta_filename}")

def main():
    # 提示用户输入目录路径
    directory = input("请输入目录路径: ")
    
    # 检查目录是否存在
    if not os.path.isdir(directory):
        print("错误: 指定的目录不存在!")
        return
    
    # 创建.mcmeta文件
    create_mcmeta_files(directory)
    print("处理完成!")

if __name__ == "__main__":
    main()
即可完成动画和发光材质的制作,打开游戏就有前面图片中的效果

不足

虽然这个可以完美兼容光影且性能最好、各版本都可以使用,但是需要多种方块来达到动态颜色变化的动画效果,而且做不到像现实中广州塔各种形式的彩虹变化效果

尝试二:使用彩色光源模组

有很多模组能够在不使用光影实现在原版的彩色光源,例如ShimmerColoredLux

huge_2025-09-24_18.48.09_0000

在这里以ColoredLux为例(因为没试过用Shimmer还原)

版本:1.12.2 + forge + HammerLib12.2.58 + ColoredLux12.6.34

ColoredLux支持玩家自制Lux Pack(类似材质包)为其他模组的方块实体绑定彩色光源效果,并且支持使用JavaScript(ES5)来控制,这就非常适合用于制作变化灯光效果了
看了下模组源码,这个模组是基于方块和实体的世界坐标来渲染光照的,那就比较容易控制哪些光源是什么颜色了
第一步:安装模组

第二步:在 1.12.2\luxpacks,即游戏目录中由ColoredLux模组自动生成的lux pack目录中,创建一个towerLight文件夹,并创建以下文件

├─icon.png
├─main.js
├─pack.json
└─scripts

icon.png 是当前lux pack的图标
pack.json 为以下内容,表示lux pack的信息,其中dependencies写了 littletiles 是因为这个尝试中,我用了littletiles这个模组还原了个2:3的广州塔,并将使用littletiles的方块作为彩色光源:

{
	"api": 2,
	"name": "towerLight",
	"authors": [ "null" ],
	"description": "",
	"dependencies": [ "littletiles" ]
}

main.js 为以下内容,表示需要绑定彩色光照的方块或实体,addTileScript第一个参数表示某个模组某个注册的tile或tileentity的完整类名(若想绑定实体,需要改为addEntityScript方法,并将实体的完整类名作为第一个参数),第二个参数表示JavaScript脚本控制的文件相对路径:

function main() {
  addTileScript("com.creativemd.littletiles.common.tileentity.TileEntityLittleTiles", "scripts/snow.js");
}

scripts 是一个文件夹,里面在创建一个 snow.js 文件,即可

第三步:使用JavaScript控制彩色光源
snow.js 文件中保存以下代码,即可实现对littletiles模组搭建的雪小方块的简单彩色光照效果(更多API请查阅源码):

点击查看代码
import com.zeitheron.hammercore.utils.color.ColorHelper;

function redColor(pos, sunAngle, aa, bb, cc) {
	var phase = 600.0 * sunAngle + 0.1 * (aa * pos.x + bb * pos.y + cc * pos.z);
	return Math.min(Math.max(2.0 * Math.sin(phase) + 0.5, 0.0), 1.0);
}

function greenColor(pos, sunAngle, aa, bb, cc) {
	var phase = 600.0 * sunAngle - 79.9999999 + 0.1 * (aa * pos.x + bb * pos.y + cc * pos.z);
	return Math.min(Math.max(2.0 * Math.sin(phase) + 0.5, 0.0), 1.0);
}

function blueColor(pos, sunAngle, aa, bb, cc) {
	var phase = 600.0 * sunAngle - 39.9999999 + 0.1 * (aa * pos.x + bb * pos.y + cc * pos.z);
	return Math.min(Math.max(2.0 * Math.sin(phase) + 0.5, 0.0), 1.0);
}

function light(world, poss, tileEntity) {
	var bld = Light.builder().pos(poss).color(ColorHelper.multiply(0x088088, 0.5), false).radius(10).build()
	var iterator = tileEntity.groups().iterator();
	while (iterator.hasNext()) {
		var parentTileList = iterator.next();
		var tileSize = parentTileList.size();
		for (var i = 0; i < tileSize; i++) {
			var littleTile = parentTileList.get(i)
			var blockName = littleTile.getBlock().toString();
			if (blockName.indexOf("now") !== -1) {
				var tileBox = littleTile.getBox();
				var center = tileBox.getCenter();
				var pos = {
					x: bld.x + center.x / 16.0,
					y: bld.y + center.y / 16.0,
					z: bld.z + center.z / 16.0,
				};

				var time = world.getWorldTime();
				var aa, bb, cc;
				var MODE_COUNT = 2;
				var SHOW_TIMES = 40;
				var sunAngle = time / 24000;
				var cyclePosition = sunAngle * SHOW_TIMES;
				var currentMode = Math.floor(cyclePosition) % MODE_COUNT;
				if (currentMode === 0) {
					aa = 0.0;
					bb = 0.0;
					cc = 0.0;
				} else {
					aa = 2.0;
					bb = -1.0;
					cc = 0.0;
				}
				var r = redColor(pos, sunAngle, aa, bb, cc);
				var g = greenColor(pos, sunAngle, aa, bb, cc);
				var b = blueColor(pos, sunAngle, aa, bb, cc);
				var bder = Light.builder().pos(poss).color(r, g, b, 1.0).radius(2).build();
				add(bder);
			}
		}

	}
}

不足

虽然这种方式能直接对世界坐标系中的方块或实体实现彩色光照,但是

  • 在数量上如果过多就会导致性能大幅下降
  • ta的光照是能透过不完整方块的,导致出现完全染色的效果
  • 模组依赖性强,且作者没有更新到新版本的意愿,不利于版本迁移
  • 不兼容光影

尝试三:使用支持彩色光照的光影

支持彩色光照的光影有很多,如ComplementaryPhoton Shaders等,文章封面的效果就是 Photon Shaders 实现的
尝试了一下,Complementary 似乎是根据屏幕位置来渲染彩色光照的,这就没办法控制不同地方的光源有不同的变化了
Photon Shaders 是采用 体素化 + FloodFill 来实现的彩色光照,确实是用坐标来定位彩色光源位置,但是是以玩家为原点的坐标系来定位,所以我们首先就需要变换坐标:

假设广州塔的位置是C,玩家的位置是B
在世界坐标系中,C(\(x_1\), \(y_1\), \(z_1\)),B(\(x_B\), \(y_B\), \(z_B\))
在以B为原点的坐标系(坐标轴方向与世界坐标系一致)中,C(\(x_2\), \(y_2\), \(z_2\))
那么转换公式为:
\(x_1=x_B+x_2\)
\(y_1=y_B+y_2\)
\(z_1=z_B+z_2\)

然后用 尝试二 代码中类似的方法魔改 FloodFill.glsl 就能实现了

不足

直接使用光影,在实现复原广州塔灯光效果的同时,实现游戏其他部分的光影效果,性能上比尝试二要好很多。美中不足的是彩色光照的体素空间有限制,一旦广州塔移出了以玩家为中心的体素空间,彩色光照效果就会消失,另外在穿透非完整方块的实现上欠佳。如果强制加大体素空间,性能会大幅下降

总结(以下为智谱清言生成)

维度 尝试一:材质包 尝试二:模组 尝试三:光影
核心技术 OptiFine的emissive自发光材质 + 自定义动画纹理 Forge模组,通过Lux Pack和JavaScript为方块/实体绑定动态彩色光源 光影包的体素化与FloodFill算法,在着色器层面实现彩色光照
实现效果 方块表面发光并播放预设的颜色动画 在世界坐标系中精确控制每个方块/实体的光照颜色、亮度和范围,可实现复杂的逻辑动画 在玩家周围一定范围内渲染逼真的彩色光照,能与光影的其他效果(如阴影、水面反射)完美融合
优点 1. 性能最佳:对游戏性能影响极小
2. 兼容性好:兼容所有支持OptiFine的光影
3. 版本跨度大:适用于所有版本
4. 实现简单:无需编程,只需制作材质和配置文件
1. 控制精度高:可针对世界坐标的每一个方块进行独立控制
2. 动态逻辑强:使用JavaScript编程,可实现基于时间、位置、玩家交互等复杂逻辑的灯光秀
3. 无需光影:在不使用或无法使用光影的设备上也能实现彩色光
1. 视觉效果最佳:光照最真实,能与场景环境(如阴影、反射)互动,沉浸感最强
2. 性能相对较好:在有效范围内,性能优于同规模的彩色光源模组
3. 集成度高:是光影效果的一部分,无需额外为灯光本身安装功能性模组
缺点 1. 动画模式单一:无法实现复杂的、有逻辑的渐变或彩虹效果
2. 资源消耗大:占用大量方块,对建筑结构有要求
3. “假”光源:仅方块自身发光,不会照亮周围环境
1. 性能瓶颈明显:当光源数量巨大时(如整个广州塔),会导致帧数急剧下降
2. 光影不兼容:与大多数光影包冲突,无法同时使用
3. 版本依赖性强:模组作者更新慢,难以迁移到新版本
4. 光照穿透问题:光线会穿透非完整方块,导致“染色”效果不真实
1. 范围限制:光照效果受限于以玩家为中心的体素空间,距离过远或塔身过高时效果会消失
2. 穿透问题欠佳:在处理非完整方块时,光照效果不能穿透
4. 硬件要求高:对显卡性能有较高要求

未来进一步实现的设想

针对每种尝试的不足,未来可以从以下几个方面进行深化和优化:

尝试一:使用材质包的动画效果

  • 设想一:结合数据包与命令方块
    • 思路:虽然材质包本身的动画是固定的,但可以利用游戏内的/execute/data命令,结合tick函数的数据包,在游戏运行时动态更换广州塔的方块。例如,预先制作好对应不同颜色帧的多个结构方块(NBT文件),然后通过命令按顺序、按位置加载这些结构,从而实现宏观上的动态变化
    • 优势:可以突破材质包动画的单一性,实现更复杂的、有逻辑的灯光模式切换(如节日模式、日常模式、彩虹模式等)
    • 挑战:命令执行和结构加载对性能有一定压力,且实现过程较为繁琐,需要精确的坐标计算和时序控制
  • 设想二:利用更高级的材质包特性
    • 思路:深入研究OptiFine的自定义物品模型或实体模型特性,或许可以将整个广州塔的灯光部分做成一个巨大的、由多个部分组成的“实体”或“物品”,然后利用材质包的条件渲染(如基于游戏时间、天气、玩家所在生物群系)来切换其显示的纹理,实现更智能的灯光变化
    • 优势:理论上可以实现比方块动画更复杂的逻辑,且对游戏世界的影响更小
    • 挑战:技术实现非常复杂,文档和案例较少,需要大量的试错和探索

尝试二:使用彩色光源模组

  • 设想一:性能优化与光源分组
    • 思路:针对性能问题,可以在JavaScript脚本中进行优化。例如,将塔上功能相似、位置相近的多个小方块“合并”为一个虚拟的、范围更大的光源进行计算和渲染,而不是为每一个小方块都创建一个光源。通过空间分区算法(如四叉树、八叉树)来管理光源,只计算和渲染玩家视野内或一定距离内的光源
    • 优势:能显著降低同时渲染的光源数量,大幅提升性能
    • 挑战:需要深厚的编程功底和对模组API的深入理解,实现复杂的空间管理和逻辑简化
  • 设想二:开发或寻找现代版的替代模组
    • 思路:鉴于ColoredLux等模组更新慢,可以尝试在Fabric或更高版本的Forge上寻找或自己开发一个功能类似的现代模组。新的模组可以利用更现代的渲染API(如Vulkan、OpenGL的更高版本特性),从底层优化渲染效率,并解决光照穿透问题
    • 优势:一劳永逸地解决版本兼容性问题,并有可能从根本上提升性能和效果
    • 挑战:开发一个功能完善的模组工作量巨大,需要专业的模组开发知识

尝试三:使用支持彩色光照的光影

  • 设想一:动态调整体素空间
    • 思路:修改光影的源代码,使其体素空间不再是固定的以玩家为中心的立方体,而是可以动态调整的。例如,可以设置一个“焦点”坐标(即广州塔的基座坐标),让体素空间始终围绕这个焦点生成和计算,或者根据玩家与焦点建筑的距离动态缩放体素空间的大小
    • 优势:彻底解决因距离过远导致灯光消失的问题,确保无论玩家在哪个角度观看,广州塔的灯光效果都是完整的
    • 挑战:需要对光影渲染管线有极深的理解,修改核心算法非常困难,且可能带来新的性能问题
  • 设想二:混合渲染方案
    • 思路:将“尝试一”和“尝试三”结合。在光影中,只处理广州塔轮廓上关键节点的、大范围的彩色光源(用于照亮环境和产生氛围)。而塔身细节上的、密集的灯光变化,则通过emissive自发光材质来实现。这样,光影负责“照亮世界”,材质包负责“点亮塔身”
    • 优势:兼顾了性能与效果。光影的计算量不会因为细节光源过多而爆炸,同时材质包的emissive材质可以完美展现细节动画,且不受体素空间限制
    • 挑战:需要精心设计和协调两种光源的亮度、颜色和范围,避免视觉冲突或效果叠加不当。这更像是一种艺术和技术的平衡,而非纯技术实现
      综上所述,每种方法都有其独特的价值和改进空间。未来的实现可以不再是单一路径的极致追求,而是根据实际需求,将不同方法的优势结合起来,创造出性能、效果和兼容性俱佳的最终方案
posted @ 2025-09-29 01:03  鼓舞人心的杰克森  阅读(21)  评论(1)    收藏  举报