How to get the author of the book by title

思路很简单,找到一个 API 即可。

此 API 接受书名(Title)作为参数,返回的数据中须包含作者(Author)字段。

经过一些探索,Google Books APIs 可以作为一个合格的解决办法。而Goodreads.com的搜索框可以是一个辅助选项。

Google Books APIs

Refer to this webpage to find more information: Google Books APIs Getting Started

Google Books has a vision to digitize the world’s books. You can use the Google Books API to search content, organize an authenticated user’s personal library and modify it as well.

Books concepts

为了能够正确处理随后返回的json数据,应当理解以下四则基本概念:

  • Volume: A volume represents the data that Google Books hosts about a book or magazine. It is the primary resource in the Books API. All other resources in this API either contain or annotate a volume.

  • Bookshelf: A bookshelf is a collection of volumes. Google Books provides a set of predefined bookshelves for each user, some of which are completely managed by the user, some of which are automatically filled in based on user’s activity, and some of which are mixed. Users can create, modify or delete other bookshelves, which are always filled with volumes manually. Bookshelves can be made private or public by the user.

    Note: Creating and deleting bookshelves as well as modifying privacy settings on bookshelves can currently only be done through the Google Books site.

  • Review: A review of a volume is a combination of a star rating and/or text. A user can submit one review per volume. Reviews are also available from outside sources and are attributed appropriately.

  • Reading Position: A reading position indicates the last read position in a volume for a user. A user can only have one reading position per volume. If the user has not opened that volume before, then the reading position does not exist. The reading position can store detailed position information down to the resolution of a word. This information is always private to the user.

Working with volumes

You can perform a volumes search by sending an HTTP GET request to the following URI:

https://www.googleapis.com/books/v1/volumes?q=search+terms

例如:搜索书目《It’s Only the Himalayas》,如果配置正确,会得到一个json,内含所有的搜索结果,一般的,认为第一个结果就是我们搜索得到的书目。

Goodreads.com

Refer to this webpage to find more information: Goodreads.com

Discover and share books you love on Goodreads, the world’s largest site for readers and book recommendations!

实例请求:搜索《Test》

对于搜索请求https://www.goodreads.com/search?q=`keyword`&qid=

只需要将关键词填入q=后

Scrape

Goodreads.com 可能有反爬机制,可以使用伪装浏览器的办法缓解一些情况:

1
2
3
4
5
6
7
8
send_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
"Connection": "keep-alive",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8"
}

result = requests.get(main_url, send_headers, proxies=proxies)

一些情况下Goodreads也无法给出正确的作者,需要使用try catch关键字捕获异常情况。

Python Code

注意事项

对于在Python中使用这两个办法,需要注意一些内容

  1. 代理可能导致错误request eof occurred in violation of protocol (_ssl.c:997)

    多见于使用的代理工具代理模式为全局代理,并且未在Python脚本中正确配置代理,尝试通过以下办法解决:

    1
    2
    3
    4
    5
    6
    7
    8
    import requests
    proxies = {
    'http': 'http://your_server:your_port',
    'https': 'http://your_server:your_port',
    }

    #仅在需要代理的请求下,填写参数proxies=proxies
    result = requests.get(Full_API_Link,proxies = proxies)
  2. Google Books APIs 所返回的json中,结果title字段下的标题并不与本地title字段相匹配。通过使用Python自带的 difflib 库实现匹配功能。

  3. 由于持续的请求,可能会导致请求失败,故使用try: except:关键字捕获异常,保证程序正常运行。

简单实现

假设:

  1. 本电脑使用的代理工具是CFW,未开启全局代理,默认端口

  2. 可以正常请求到对应书目,遍历json找到最为匹配的标题。

  3. Google Books APIs 无法正确获得作者,进而使用Goodreads尝试获取之。

代码:

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
import requests
proxies = {
'http': 'http://localhost:7890',
'https': 'http://localhost:7890',
}
import json
import difflib、
from bs4 import BeautifulSoup

def UrlToSoupAdvanced(Url:str):
main_url = Url
print(main_url)
send_headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36",
"Connection": "keep-alive",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8"
}

try:
result = requests.get(main_url, send_headers, proxies=proxies)
except :
time.sleep(5)
return "[Unknown][Goodreads]Fail to request the link"

return BeautifulSoup(result.text, 'html.parser')

def TwoStrMatch(string1:str,string2:str):
result = difflib.SequenceMatcher(None, string1, string2).quick_ratio()
print(string1 + "====" + string2 + "====" + str(result))
return result

def GetAuthorByTitleUsingGoogleBooksAPI(Title:str):
Google_Book_APIs_Head = "https://www.googleapis.com/books/v1/volumes?q="
API_KEY = "AIzaSyCdlpdS8EWgKIN6EW95fwjiLqDkkiIA8Pg"
Search_Data = Title

#https://www.googleapis.com/books/v1/volumes?q=A%20Summer%20In%20Europe+intitle:keyes&key=AIzaSyCdlpdS8EWgKIN6EW95fwjiLqDkkiIA8Pg
#https://www.googleapis.com/books/v1/volumes?q=a summer in europe&printType=books&intitle:a summer in europe

Full_API_Link = Google_Book_APIs_Head + Search_Data
#Full_API_Link = Google_Book_APIs_Head + "It's_Only_the_Himalayas"

print("The request link is " + Full_API_Link)
try:
result = requests.get(Full_API_Link,proxies = proxies)
except :
time.sleep(5)
return GetAuthorByTitleUsingGoodreadsSearch(Title)



return_json_dict = json.loads(s = result.text)
if 'items' in return_json_dict:
return_items = return_json_dict['items']
else:
return GetAuthorByTitleUsingGoodreadsSearch(Title)

#对于返回的搜索结果
for book in return_items:
current_volumn = book['volumeInfo']
current_title = current_volumn['title']

if 'authors' in current_volumn:
current_authors = current_volumn['authors']

#如果标题直接匹配,正常返回
if (TwoStrMatch(current_title, Title) > 0.85):
return str(','.join(current_authors))

if('subtitle' in current_volumn):
#如果加上副标题是匹配的,则正常返回
if (TwoStrMatch(current_title + current_volumn['subtitle'], Title) > 0.85):
return str(','.join(current_authors))

return GetAuthorByTitleUsingGoodreadsSearch(Title)

def GetAuthorByTitleUsingGoodreadsSearch(Title:str):

Full_Search_Link = "https://www.goodreads.com/search?q=" + title +"&qid="


try:
soup = UrlToSoupAdvanced(Full_Search_Link)
except:
return "[Unknown][Goodreads]Fail to request the link"

try:
author = soup.find('span', itemprop="author").div.a.span.string
except:
return "[Unknown][Goodreads]No correct Author"

if(author == None):
return "[Unknown][Goodreads]No correct Author"
return author

HDRP光照系统讲解

场景中的GameObejct挂载Volume组件,生成对应的Profile资源,生成的资源文件会被自动放在Scene路径下与场景名相同的文件夹中。为了使得HDRI Sky和阴影配置真正生效,需要在Light界面中将 Scene Settings Profile 这个Volume资源与之关联。

Volume

Directional Light

平行光灯的Mode为Realtime,所以它提供直接照明。只有光线照到之处才变亮。因为没有间接光照信息。ReflectionHDRP 中 的反射层级先后顺序是:先看场景中有没有Screen Space Reflection(简称SSR,屏幕空间反射),如果没有再找Reflection Probe(反射探针),如果还没有就反射天空。

Ambient Occlusion

通过明暗(光影变换)表示几何细节
c2cf9d78072781eb5db20b4055aa7d61.png
2f6fdb8fd17b1c798280345947236d70.png

环境光遮蔽针对当前镜头中全屏画面进行计算。通过计算场景中的灯光和物体周围环境,让裂缝,孔洞和物体之间交接处等区域的颜色变得更黑。在真实世界中,这些地方通常因为受到周围物体的遮蔽而变得更黑。

Anti-Aliasing

在HDRP中,我们可以使用Camera组件为画面添加抗锯齿效果。FXAA对性能的消耗少画面会变糊,其次是TAA,性能消耗最大的是SMAA(Enhanced Subpixel Morphological Antialiasing)抗锯齿效果最好的是TAA,其次是SMAA(对静态锯齿效果很好),抗锯齿效果最差的是FXAA。

Physical Light Units and Intensities

Visual Environment 组件中选择了HDRI作为天空的类型

Lux光照强度模式,此处的“光强”可以理解为整个场景的明暗,和直接光照导致的高光、暗部是有区别的。

Lux 50000:

9dee749b5fd822c3a38634ea79c95fbb.png

Lux 10000:

54bb782be1a213c6cc7a1488d263113f.png

Lux 1000 + Directional Light

b9bb9307d901fde73d660ac611b36c66.png

Lux 5000 + Directional Light + Indirect Multiplier > 1

6ac525aec2a04ab7e897678176f7415c.png

Lux光照单位表示的光照信息只有方向没有强度衰减信息,也就是说此单位适合用于表示太阳光这一类对于地球来说只有角度变化,没有强度变化的光照信息。

Light Probe

光照探针被认为是一种价廉物美,为动态物体提供间接光照信息和生成动态阴影的高性能解决方案

Bake

确保所有的静态物体被标记为Static

d196d81482cdb93b85c8ea6ef4923e58.png

对于光照烘培来说,只要确保勾选Contribute GI(贡献全局关照)和 Reflection Probe Static 选项即可。

Environment(HDRP)

可以指定所用的环境光Volume,并指定相关联的Sky作为环境光的来源。

Mixed Lighting

要想烘培全局光照,必须在这里勾选Baked Global Illumination(烘培的全局光照) 选项,然后选择光照模式: Baked Indirect 或者 Shadowmask

Baked Indirect:仅仅烘培间接光照信息

Shadowmask

  • 提供最逼真的阴影效果。不仅烘培间接光照信息,也会在Lightmap(光照贴图)中把阴影烘培进去。

  • 适用于远处存在可见物体的场景,比如在高端机器上运行的开放世界游戏。

Lightmapping Settings

Filtering

可以选择一种Denoiser(降噪算法),可以分为Direct Denoiser、Indirect Denoiser和Ambient Occlusion Denoiser选择不同的降噪算法。

一般建议是根据不同的GPU平台为这三项选择同一种降噪算法。

NVIDIA :Optix

AMD:Radeon Pro

Intel:OpenImageDenoise,基于CPU的降噪算法,可适用于所有平台。

Lightmap Padding

控制光照贴图中,按照UV分开的光照贴图区域之间的间隔太小。

如果此间隔太小,有可能造成光照贴图边缘的颜色渗透。

但是如果间隔太大,又会浪费光照贴图空间,增加贴图数量,导致光照贴图在内存中的占用增大。

Indirect Lighting Controller of Volume

间接光照信息被烘培进光照贴图中后,可以使用此Override增强整个场景的间接光照强度。

Indirect Diffuse Intensity 间接光漫反射强度

HDRP将会由烘培获得的光照贴图和光照探针上的数据乘以这个值。‘

如果值为0,那么

IDI = 4 ISI = 1551d8ea8b610f375ac4db13b9ce24126.pngValue = 16 ISI = 1ee8062c2fc3e5aec87042648581efc43.png

Indirect Specular Intensity 间接光高光强度

HDRP会将任何类型(Baked、Custom或者Realtime)的反射探针(Reflection Probe)上的数据乘以这个值。

IDI = 16 ISI = 1(same as above one)ee8062c2fc3e5aec87042648581efc43.png

IDI  =16 ISI = 104316863f946868cdff2b03003ddc6ce9.png

IDI = 0 ISI = 0

该画面中没有任何来自间接光照的漫反射和高光,没有被平行光找到的地方漆黑一片。9199da6a2fe393974a58302059e378af.png

IDI = 0 ISI = 1

该画面中只有来自间接光照的高光,没有漫反射。

d6c0d281c4393b981101c601e0de04c9.png

IDI = 1 ISI = 0

该画面中只有来自间接光照的漫反射,没有高光。

047cd7d712df383557a4a5cde4f694bf.png

IDI = 1 ISI = 1

该画面中只有来自间接光照的漫反射,也有高光。

b454a52aa49bac9b8c93ba787bda6005.png

处理阴影与环境光遮蔽

Directional Light

Volume 框架

Volume 框架是HDRP最重要的组成部分之一,其作用如下:

  • 为场景设置来自天空盒的环境光照

  • 设置各种阴影效果,包括场景中的Contact Shadow 和 Micro Shadow

  • 设置场景中的雾效

  • 设置基于屏幕空间的反射(SSRelection)和折射(SSRefraction)、环境光遮蔽(Ambient Occlusion)

  • 设置后处理效果

  • 设置实时光线追踪

在同一个场景中可以添加多个带Volume组件的GameObject。每个Volume的模式可以按照需要设置成Global 或是 Local

Global意味着这个Volumn针对整个场景生效。不管相机朝向哪里,Global模式的Volumn都会被计算在内。

Local模式的Volumn则只能在它的碰撞体作用区域内生效。一般Box Collider即可满足碰撞体需要。

Exposure

曝光指的是相机感光元器件接受的光线强度。曝光的数值越高,相机接受的光线强度越大。

参数说明

Exposure重载包括四种模式:Fixed、Automatic、Curve Mapping 和 Use Physical Camera

Fixed 固定曝光

用于曝光变化不大的场景,也就是场景中的各个区域有类似的曝光强度。

可以使用多个被设置为Local模式的Volume,通过Blend Distance(混合距离)来融合切换不同的Volume的曝光。

由于HDRP的光照系统是完全基于物理的,所以可以参考物理世界真实的曝光值。

5cfd281ebd4bad3543fc5a737f12e351.png

Automatic 自动曝光

根据当前相机的位置信息和相关场景信息来自动调节曝光。非常适用于曝光随位置不同而发生变化的场景,例如从光照强烈的户外进入阴暗的室内或者从山洞中移动到山洞外

可以模拟现实中人眼适应不同曝光的情况。

Limit Min & Limit Max

分别用于控制场景自动曝光的最小值和最大值。可以用这两个值分别调节当相机处于场景的暗部和亮部时整体的曝光。

99b37196985c18f0e7285b3578469b4e.png

当画面靠近场景中的亮部,周围的环境会变暗,但是环境也许不应该“那么”暗。此时可以将Limit Max 调低,以提升整个画面的曝光。f688c683062bbe0c79c0bb2e09b5814d.png

当画面处于场景的暗部,比如在黑暗的角落朝外看,那么外部环境会变亮,可能导致画面亮部曝光过度(月光即使很亮也不应该像白天)此时可以将LImit Min 调高,降低整个画面的曝光。

Curve Mapping 曲线控制曝光

X轴代表目前场景的曝光

Y轴代表我们想要的曝光

专家使用。

Use Physical Camera 物理相机控制曝光

通过物理相机的Iso、Shutter Speed 和 Aperture 参数来控制曝光。

Volume 界面中只剩下Compensation(补偿)这一项。

Metering Mode

用于控制如何采样用于曝光的计算场景中的灯光。

  1. Average 针对整个画面进行采样,结果稳定

  2. Post 针对画面中央区域采样。如果画面中央的采样区域较暗,整个画面可能曝光过度。

  3. Center Weighted

Visual Environment

Intro to HDRP & DXR

从2018版本开始,Unity Editor中共有三套渲染管线。

HD Render Pipeline Asset

HDRP配置文件(HD Render Pipeline Asset)管理HDRP项目的所有渲染功能。HDRP会用这个配置文件生成一个HDRP渲染管线的实例,而这个渲染管线的实例包含用于渲染的中间资源。

为了满足项目开发中不同的需求,HDRP还提供了另外两种渲染方式。

针对不同平台使用不同的HDRP配置文件

在同一个HDRP项目中可以创建多个HDRP配置文件,针对不同的计算平台应用不同的HDRP配置文件。

要针对不同的平台使用不同的设置,需要将对应的HDRP配置文件关联到不同的质量等级上。

质量设置里面的HDRP配置文件会覆盖默认的配置文件。

Frame Settings

帧设置针对的是场景中的Camera、Baked or Custom Reflection 和 Realtime Reflection 的相关设置。后面两个反射相关的设置应用在Reflection Probe上

帧设置的优先级低于HDRP配置文件,也就是说,如果在HDRP配置文件中没有打开某项功能,那么帧设置中对应的功能就会自动被禁用。

帧设置可以让我们为不同的相机和反射探针启用/禁用不同的HDRP功能组合。

Volume 框架

Volume 的作用是通过调整各项HDRP功能的参数,影响相机所看到画面的最终渲染效果。

dd003dc04f47c381deb98503587eb575.png

  • 每个HDRP项目中可以有多个HDRP配置文件

    每个配置文件对应不同的画质或目标平台。但是HDRP项目每次只能使用一个HDRP配置文件,也无法在运行时切换HDRP配置文件。

  • HDRP会为Camera(相机),Backed or Custom Reflection(烘培或自定义反射)和Realtime Reflection(实时反射)提供一套默认的帧设置。如果在HDRP配置中没有被启用,就会在帧设置里被禁用。

  • 注意,在HDRP配置文件中已经启用的功能,也要确保默认帧设置中启用了相关功能。即在HDRP配置文件中启用了某个功能,但是没有在帧设置中启用它,那么在项目中也是无法使用它。

  • 可以为场景中一个(或者多个)相机和反射探针自定义帧设置。 如果在这些自定义帧设置中启用某个功能(前提是在HDRP配置文件中已经启用),那么自定义帧设置中的配置信息会覆盖(Override)默认帧设置中的配置信息。

  • 可以为同一个场景这创建多个Volume。这些Volume的模式可能是全局(Global)的或者是本地(Local)的。如果在当前活跃相机的帧设置(如果没有启用自定义帧设置,就使用默认帧设置)中没有启用某个功能,比如Fog,那么在与此相机相关的Volume中调整Fog参数值就没有意义。

HD Render Pipeline Asset中的7类参数

Rendering

  1. Color Buffer Format

    出于对性能的考虑,HDRP默认使用R11G11B10格式(不包含Alpha通道)

    如果要把HDRP渲染的画面合成到另外的图片上,就需要包含Alpha通道,这时就要选择R16G16B16A16格式。带有Alpha通道的格式会对性能造成一定的影响。

    如果需要将R16G16B16A16格式作为最终渲染输出的格式,那么在Post Processing的Buffer Format中也要选择相同的格式。

  2. Lit Shader Mode

    Lit Shader 是HDRP材质使用的默认着色器。可以选择以下三种模式:

    1. Forward:Lit Shader仅使用前向渲染

    2. Deferred:Lit Shader会使用延迟渲染,一些高级材质还会使用前向渲染。

    3. Both:延迟和前向渲染都可用

      选择Both模式可以通过自定义帧设置为相机选择Deffered或者Forward渲染。不过此模式会让HDRP为两种渲染方式都编译相关的着色器变体。

      如果选择前向渲染或Both模式,则可以选择MSAA

  3. Motion Vector

    HDRP可以在屏幕空间反射和运动模糊中使用运动矢量。通过Camera组件启用的TAA必须使用运动矢量才能工作。如果禁用此选项,则运动模糊和TAA功能将不会正常工作,屏幕空间反射则会用低质量渲染模式。

  4. Runtime Debug Display

    启用该选项后可以在运行时显示灯光和材质的属性信息。正式出包时建议禁用。

  5. Dithering Cross-fade 平滑转换

    这是与Game Object的LOD转换相关的功能。启用该选项后可以让HDRP在做LOD转换时进行平滑的转换。

  6. Terrain Hole 地形洞

    启用该选项后可以显示地形上的凹陷孔洞。如果禁用此选项,则地形上的孔洞不会显示。

  7. 如果你的场景中没有使用透明材质或者没有在Lit材质中使用相关选项,则可以禁用以下选项以减少构建时间。

    1. Transparent Backface 透明背面
    2. Transparent Depth Prepass 透明深度预处理
    3. Transparent Depth Postpass 透明深度后处理

    4e7db5f02099684052b94ad28a7b96ec.png

  8. Custom Pass 自定义通道

    如果没有使用Custom Pass 功能,禁用此功能可以节约内存。

  9. Realtime Raytracing 实时光线追踪

    如果要在HDRP项目中使用实时光线追踪功能,则需要先启用此选项。

  10. LOD Bias LOD 偏差

    场景中的相机会使用此数值来计算LOD偏差。

  11. Maximum LOD Level 最大LOD级别

    用于设置相机支持的最大LOD级别。

  12. Decals 贴花

    1. Draw Distance 用于定义相机离开物体多远以后不再渲染贴花。

    2. Atlas Width和Atlas Height 用于设置纹理图集的宽度和高度。这个纹理图集用于保存场景中所有投射在透明表面上的贴花。

    3. Metal and Ambient Occlusion Properties 启用该选项后,贴花能够影响材质上的金属高光和环境光遮蔽。

    4. Maximum Clustered Decals on Screen 屏幕上能够同时显示的贴花数量(这些贴花影响的是透明表面)。
      e9609527dd29619d4590b419e98c8923.png

  13. Dynamic Resolution 动态分辨率

  14. Low res Transparency 低分辨率透明

    启用该选项后使用低分辨率的透明效果。

Lighting

  1. SSAO(Screen Space Ambient Occlusion)启用此选项后可以为场景添加基于屏幕空间计算的环境光遮蔽效果。可以在Volume中的Ambient Occlusion Override中对效果进行调整。

  2. Volumetrics 体积光

    为场景中的灯光和雾效增加体积光效果。

  3. Light Layers 光照层可以

    在这里启用/禁用光照层(Light Layers)功能。此功能可以让场景中的光源只照亮指定的物体,忽略无关的物体。在HDRP Global Settings -> Layers Names 中可以见到对应层

  4. Cookies 光线遮罩

  5. Reflections 反射
    9494534745278471e7e3a3217ea91803.png

  6. Sky 天空
    2524c9d6c97cc60d8bb2ac333a2fe060.png

    1. Reflection 当场景中没有如何反射探针可以用于计算物体表面的反射信息时,HDRP会使用天空盒Cubemap来计算反射信息。Reflection Size可以控制用于计算反射信息的天空盒Cubemap的分辨率。此分辨率斌不会影响天空盒本身的质量。

    2. Lighting Override Mask 可以让环境光照和天空背景进行分离。如果在此指定了一个Layer而不是使用默认的Nothing,那么HDRP会在场景中寻找与此Layer相关联的Game Object,如果找到的GameObject中包含Volume组件而且可以对当前相机产生影响,那么HDRP就会使用这些Volumn中的信息来计算环境光照。

  7. Shadow 阴影
    d1b2413c2a758ae18a76e6a00215c0a2.png

    1. Shadowmask

      控制Shadowmask光照模式(Shadowmask Lighting Mode)的启用/禁用。

    2. Maximum Shadows on Screen 同屏显示最大阴影数量

      超过这里设定的阴影数量之外的阴影将不被渲染。

    3. Filtering Quality 过滤质量

      选择高质量可以提升阴影质量,减少阴影边缘的锯齿。在Forward模式和Both模式下,可以选择Low,Medium和High三档质量。在Deferred模式下,只能使用Medium质量。

    4. Screen Space Shadows

      启用该选项后,HDRP会在一个单独的通道中计算基于屏幕空间的阴影。

    5. Use Contact Shadows 使用接触阴影

      此处可以选择Low、Medium、High质量。然后在Light组件中可以选择可用的接触阴影质量。

      如果在HDRP配置文件中不勾选上述任何一项,那么只能在Light组件中选择Custom选项。

      要使用接触阴影,需要在Default Frame Settings中启用Contact Shadows选项。

Lighting Quality Settings

光照质量的相关设置,配合Lighting中的参数使用,调整相应数值以达到质量与性能的平衡。

Material

  1. Available Material Quality Levels 可用材质质量等级

    默认所有材质质量都可以使用

  2. Default Material Quality Level 默认材质质量等级

  3. Distortion 变形

  4. Subsurface Scattering 次表面散射

    可以很好的表示光在材质内多次反弹折射等等效果,玉石、翡翠等应用此效果明显。

  5. Fabric BSDF Convolution                             

    若启用此选项,则在使用Fabric材质时,HDRP会单独为织物着色器(Fabric Shader)计算一份反射探针数据,用于生成更准确的光照效果。不过这样做会导致项目中存在两份光照数据,也会导致目前可见的反射探针数量减少一半。

  6. Diffusion Profile List 漫射配置文件列表

    在此保存用于控制次表面散射效果和半透明效果的Diffusion Profile。一个HDRP配置文件最多可以保存15个Diffusion Profile。

Post-processing

  1. Grading LUT Size & Grading LUT Format

    Size用于控制颜色分级时所用的LUT(Lookup Texture)的大小。默认数值32提供了比较平衡的速度和质量表现。

    Format用于设置LUT的编码格式。可以选择R11G11B10R16G16B16A16或者R32G32B32A32格式。精度越高的格式颜色越精准。

Post-processing Quality Settings 后处理质量设置

与光照质量设置类似,可以为后处理效果设置采样值。

e21c555d3500b554ee4bec37f8590f04.png

目前可以设置Depth of Field、Motion Blur、Bloom、Chromatic Aberration(色差)

XR设置

HDRP在特定的平台上输出VR应用

PC需要DX11支持

Ray Tracing

Unity 支持的光追是基于HDRP的,一个光追的Unity项目,首先是一个HDRP项目

Camera

参考视频:# 04.相机系统 | GAMES204-计算成像

Nautilus Eye 大王乌贼 小孔成像阶段

tapetum incidum 反射层,提高夜视能力

Compound Eye 光场相机

Human Eye

Visual acuity: 20/20 is ~1 arc min

Field of view: ~190° monocular, ~120°binocular,~135° vertical

Temporal resolution: ~60Hz(depends on contrast, luminance)

Dynamic range: instantaneouse 6.5 f-stops,adapt to 46.5f-stops

Color: everything in the CIE xy diagram; distances are linear in CIE Lab

Depth cues in 3D displays: vergence, focus, conflicts,

Accommodation range: ~8cm to ∞,年龄越大看看近能力越差

Camera Optics: Lens

ec646e532214e77599a4bcecd23f81be.png

a8047fc3ad676d0b026095db6b8b5f19.png

Aberrations

Distortion

2022-09-28-15-40-12-image.png

Aperture

2022-09-28-15-41-50-image.png

2022-09-28-15-45-18-image.png

Unit: f-number N = f/D

f 焦距,D aperture 直径

D 越小,进光量越大,景深越小

Camera Depth of Field

物距、Aperture Size、放大率

M放大率越大,景深越小

25ee6b05eabc4a82a38d73a3428931e0.png

2022-09-28-15-47-03-image.png

hyper plane

b6ea4cceb87c62e7a74dbe0e4b696757.png

光圈小会带来曝光时间长的问题

Field of View

超广角,长焦

824cb200b298e4cb4590c2af2ed6e4f3.png

Focal length 是固定的,Sensor 是固定的

f27778e65d026159398b0d7e87ab3c4f.png

a6b225fadce5b51fe63034a26bc7c05b.png

Diffraction Limit 衍射极限

5f726342e3625ca0ba111aefc6212dea.png

Dolly zoom

Sensor 传感器

非线性的 曝光程度(Log Exposure)和曝光密度(Density)

84dd9d32bb15d0ac9e3fa0475dedca08.png

HDR imaging 高动态范围

ac9925536f0387a309d0128aac782576.png

415bf0d42c896f3e46f1a614d4403756.png

ISO 相对线性
5375eb11736d57918a6387a4a0c5da08.png
电子快门对于运动快的物体会导致拉伸变形

Intro to Android

Application Components

Services

A service is an component that runs at the background

E.g. music play at the background

Service is not a thread

Broadcast Receiver

Receives broadcast from Android system and other apps and responses with pre-coded actions.

Apps are required to regist to get the broadcast

Example: When the battery level changes, the system broadcast a message.

Activity

Offen refer to one interface on your phone.

Primary class for interacting with user.

For example, Wechat:

  • When you click on the app launcher, its corresponding greeting page
    will be shown to you.

    Android system invokes the main activity of the Wechat.

  • Apps that request for online payment (such as railway ticket
    payment) can directly reach the payment page.

    Another app invokes the payment activity of Wechat.

  • Apps that request for “share on moments” can directly invoke the
    moments sharing page of Wechat.

    Another app invokes the “share on moments” activity of Wechat

Back Stack

When a new activty is launched, the previous activity will be paused and sent to the top of the back stack

3074c66675d6c28231d50f91cb67a2ae.png

Life circle

  • Runing: The activity is on the top of the screen and gained focus. Running will not be killed.

  • Paused: The activity is on the top of the screen and gained focus.

  • Stopped: The activity is completely covered by another running activity.

  • Destroyed

When running short of memory, a stopped activity is more likely to get killed than a paused/running activity.

5f74b1146359b2da39a899a5156f07a6.png

Good implementation of callback functions can make your app more robust and performant.

Possible issues with a bad implementation:

  • Crashing if the user receives a phone call or switches to another app while using your app.

  • Consuming valuable system resources when the user is not actively using it.

  • Losing the user’s progress if they leave your app and return to it at a later time.

  • Crashing or losing the user’s progress when the screen rotates between landscape and portrait orientation.

CallBack Funtions: Typical Uses
  • onCreate(): Initial setup, load persistent state.

  • onRestart(): read cached state

  • onStart(): reset application

  • onResume(): start foreground-only behaviors

  • onPause(): shutdown foreground-only behaviors

  • For example: temporarily stop UI animations.

  • onStop(): cache state

  • onDestroy(): save persistent state

The above points are very general. Carefully design your app and keep the life cycle graph in mind.

Create Activities

23c0e5afcf14c33b320504479143645f.png

  1. Create a new Activity class. Which either inherits Android.app.Activity or its subclasses.

  2. Override Activity.onCreate().

  3. Create a layout XML file in res/layout and use setContentView() to load this layout.

  4. Register the new activity in AndroidManifest.xml.

    If it is a main activity, you need to add a special section in the manifest file.

    5f50cb9f027a1bd432f0dc1ea3bb051c.png

Activities can be started by calling the function

1
startActivity(Intent intent)

To call Activity2 Inside an activity, do:

1
2
3
4
Intent intent =
new Intent(this, Activity2.class);

startActivity(intent);

The name of the target activity is not always explicitly specified. For instance, to let Android system choose an suitable activity for sending email (in Lecture 9):

1
2
3
Intent intent = new Intent(Intent.ACTION_SEND);
Intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

Obtain vals from another activity

Sometimes we wish to obtain results from another activity. We need to start the activity using

1
startActivityForResult()

You must also implement the function below to get the return result

1
onActivityResult()

e3abb411a93727fb5764515c2ad064d3.png

Closing Activities

Android will automatically manage the life cycles of your activities.

You can destroy the current activity manually by calling finish().

To finish an activity that you previously invoked with

1
2
startActivityForResult(Intent,
int), use finishActivity(int requestCode)

Can be handy when you want to make sure that the user won’t return to this activity in the future.

Passing Data between Activities

f7362876239d8ffc3874022bfed42ee0.png

Bundle and Intent is actually the same thing.

Console output

Your console output (System.out) can be seen from the “run” window in Android Studio.

You should normally use Log class instead, though:

Android Logcat | Log.v(), Log.d(), Log.i(), Log.w(), Log.e() - EyeHunts

Photon with Unity 随笔

Intro

Photon Unity Networking (PUN) is a Unity package for multiplayer games. Flexible matchmaking gets your players into rooms where objects can be synced over the network. RPCs, Custom Properties or “low level” Photon events are just some of the features. The fast and (optionally) reliable communication is done through dedicated Photon server(s), so clients don’t need to connect one to one.

相关代码可

Refer to the link below to view in the Asset Store (PUN2)

PUN 2 - FREE | 网络 | Unity Asset Store

This note is based on PUN2 Version 2.41 - August 2, 2022

Dev Region

一般的,如果配置中空缺此项,游戏会自动连接延迟低的服务器,可能会导致无法观察到彼此的房间。推荐将Dev Region设置为kr或者jp

General Functions

Common Use Variables

PhotonNetwork.CurrentRoom记录了当前连接的房间的信息

PhotonNetwork.PlayerList记录了连接到当前房间的玩家信息列表

PhotonNetwork.IsMasterClient当前客户端是房主

PhotonNetwork.NickName本地玩家的昵称

General Process

导入Photon的包后,可以使用PUN Wizard完成初始化,需要有自己的AppID。稍后可以通过修改Assets/Photon/PhotonUnityNetworking/Resources/PhotonServerSettings.asset路径下的配置文件进行配置。

一般的,连接的级别大致分为Server(Master)-Lobby-Room

下列代码给出了连接服务器、创建、加入房间的代码流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//MonoBehaviourPunCallbacks

PhotonNetwork.ConnectUsingSettings();
public override void OnConnectedToMaster(){}

PhotonNetwork.CreateRoom(roomNameInputField.text);
public override void OnCreatedRoom(){}
public override void OnCreateRoomFailed(short returnCode, string message){}

PhotonNetwork.JoinRoom(info.Name);
public override void OnJoinedRoom(){}

PhotonNetwork.LeaveRoom();

public override void OnRoomListUpdate(List<RoomInfo> roomList){}
public override void OnMasterClientSwitched(Player newMasterClient){}
public override void OnPlayerEnteredRoom(Player newPlayer){}

下面给出了实际的代码应用

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
public class Launcher : MonoBehaviourPunCallbacks
{
void Start()
{
//通过配置连接到服务器
PhotonNetwork.ConnectUsingSettings();
}

public void CreateRoom()
{
//创建房间,需要指定房间名
if (string.IsNullOrEmpty(roomNameInputField.text))
{
return;
}
PhotonNetwork.CreateRoom(roomNameInputField.text);
}

//加入房间,以房间名为参数
public void JoinRoom(RoomInfo info)
{
PhotonNetwork.JoinRoom(info.Name);
MenuManager.Instance.OpenMenu("Loading");
}

//退出房间
public void LeaveRoom()
{
PhotonNetwork.LeaveRoom();
}

//重要的回调方法:
//成功连接到服务器后调用
public override void OnConnectedToMaster()
{
Debug.Log("[Network]Connected to Master");
PhotonNetwork.JoinLobby();

//同步场景
PhotonNetwork.AutomaticallySyncScene = true;
}

//本地客户端连接到房间时调用
//PhotonNet
public override void OnJoinedRoom()
{
roomNamePUN.text = PhotonNetwork.CurrentRoom.Name;

Player[] players = PhotonNetwork.PlayerList;

foreach (Transform trans in playerListContent)
{
Destroy(trans.gameObject);
}

for (int i = 0; i < players.Count(); i++)
{
Instantiate(playerListItemPrefab, playerListContent).GetComponent<PlayerListItem>().SetUp(players[i]);
}

//使得按钮只对创建客户端可见
startGameBtn.SetActive(PhotonNetwork.IsMasterClient);
}

public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
//PhotonNetwork.countOfRooms != PhotonNetwork.GetRoomList();
Debug.Log("[Network]Room List is Updated: " + roomList.Count() + " in total");

//先清理已有的房间列表
foreach (Transform item in roomListContent)
{
Destroy(item.gameObject);
}


for (int i = 0; i < roomList.Count(); i++)
{
if (roomList[i].RemovedFromList)
continue;
Instantiate(roomListItemPrefab, roomListContent).GetComponent<RoomListItem>().SetUp(roomList[i]);
}
}

//当房主发生了变化
public override void OnMasterClientSwitched(Player newMasterClient)
{
startGameBtn.SetActive(PhotonNetwork.IsMasterClient);
}

//离开房间时调用
public override void OnLeftRoom()
{

}

//房间消息列表更新时调用(如创建了新房,已有的房间关闭)
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
//PhotonNetwork.countOfRooms != PhotonNetwork.GetRoomList();
Debug.Log("[Network]Room List is Updated: " + roomList.Count() + " in total");

//先清理已有的房间列表
foreach (Transform item in roomListContent)
{
Destroy(item.gameObject);
}


for (int i = 0; i < roomList.Count(); i++)
{
if (roomList[i].RemovedFromList)
continue;
Instantiate(roomListItemPrefab, roomListContent).GetComponent<RoomListItem>().SetUp(roomList[i]);
}
}

//当一个远程玩家进入房间时调用
public override void OnPlayerEnteredRoom(Player newPlayer)
{
Debug.Log("[Network]" + newPlayer.NickName + " connected, in room: " + PhotonNetwork.NickName);
Instantiate(playerListItemPrefab,playerListContent).GetComponent<PlayerListItem>().SetUp(newPlayer);
}
}

当玩家都进入同一个房间时,可以考虑使用来同步场景

1
PhotonNetwork.LoadLevel(1); 

PlayerProperties

PlayerProperties 是一种同步玩家间状态的方法,以下是代码示例,目的为同步玩家手中所持有的武器:

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
//区分C#内置的ashtable与Photon提供的Hashtable
using Hashtable = ExitGames.Client.Photon.Hashtable;

public class PlayerController : MonoBehaviourPunCallbacks
{
PhotonView PV;

private void Awake()
{
PV = GetComponent<PhotonView>();
}

//同步武器的实质是同步所持武器的itemIndex
void EquipItem(int _index)
{
itemIndex = _index;

//当我们装备一个武器时,首先查看是否是localPlayer,若是,将把这个切换到的武器index发出去
if (PV.IsMine)
{
Hashtable hash = new Hashtable();
hash.Add("itemIndex", itemIndex);
PhotonNetwork.LocalPlayer.SetCustomProperties(hash);
}
}

//所有在房间内玩家会收到同步信息,调用此回调。参数中给出了属性被改变的玩家,属性新值
public override void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
{
//本地已经切换了武器,无需再次同步 && 在客户端看,仅仅改变对应玩家的index,而不是所有玩家
if (changedProps.ContainsKey("itemIndex")&&!PV.IsMine && targetPlayer == PV.Owner)
{
EquipItem((int)changedProps["itemIndex"]);
}
}
}

[PunRPC]

Example

需要Pun远程调用的方法,应当给与PunRPC标签,下面给出造成伤害、收到伤害的流程:

单发枪类,能够开火

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SingleShotGun : Gun
{
//枪开火,进行射线检测
void Shoot()
{
//从摄像机的近剪裁面的中点向着远剪裁面的中点绘制一条射线
Ray ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f));
ray.origin = cam.transform.position;
if (Physics.Raycast(ray, out RaycastHit hit))
{
Debug.Log("[Weapon]Ray cast hit " + hit.collider.gameObject.name);
hit.collider.gameObject.GetComponent<IDamageable>()?.TakeDamage(((GunInfo)itemInfo).damage);

//生成 Bullet Impact
PV.RPC(nameof(RPC_Shoot),RpcTarget.All, hit.point,hit.normal);
}
}
}

PlayerController类,每一个玩家角色GameObject都挂载此类

使用nameof()可以避免可能的拼写错误

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
//本地被击打的物体实现IDamageable接口,调用TakeDamage()方法
public class PlayerController : MonoBehaviourPunCallbacks, IDamageable
{
public void TakeDamage(float damage)
{
//PV.Owner指定了被打击的角色,damage为此次命中的伤害
PV.RPC(nameof(RPC_TakeDamage), PV.Owner, damage);
Debug.Log("[Combat]" + "I(" + PhotonNetwork.NickName + ") Try to hurt " + PV.Owner.NickName + " at damage " + damage);
}

//对于被打击的玩家来说,其RPC方法被调用了
[PunRPC]
void RPC_TakeDamage(float damage, PhotonMessageInfo info)
{
Debug.Log("[RPC][Combat]" + PV.Owner.NickName + " Take damage " + damage);
currentHealth -= damage;

healthbarImage.fillAmount = currentHealth / maxHealth;

if (currentHealth <= 0)
{
Die();
//找到发送信息者,追溯伤害来源
PlayerManager.Find(info.Sender).GetKill();
}
}
}

Bullet Impact

通过Photon同步大量生成的GameObject是昂贵的,一般使用RPC调用对应方法,生成物体。

1
2
3
4
5
6
7
8
9
10
11
12
[PunRPC]
void RPC_Shoot(Vector3 hitPosition,Vector3 hitNormal)
{
Collider[] colliders = Physics.OverlapSphere(hitPosition, 0.3f);
Debug.Log("[RPC][Combat]" + hitPosition);
if (colliders.Length != 0)
{
GameObject bulletImpactObj = Instantiate(bulletImpactPrefab, hitPosition + hitNormal * 0.005f, Quaternion.LookRotation(hitNormal, Vector3.up) * bulletImpactPrefab.transform.rotation);
Destroy(bulletImpactObj,10f);
bulletImpactObj.transform.SetParent(colliders[0].transform);
}
}

Photon View

View ID[1..999] 对于每个Photon View 应当是 unique 的

Photon.Instantiate

Instantiate prefabs from Photon is not the same as instantiate a prefab from Unity normally

为了能够在多种平台上成功的加载预制体,尤其是此挂载了PhotonView的预制体(而不是仅仅在Editor中),

PhotonPrefabs must be in the resources folder.

Because Unity automatically excludes any file not referenced in the editor from the final build, and we don’t reference PhotonPrefabs. We use strings.

1
PhotonNetwork.Instantiate(Path.ComPhotonNetwork.Instantiate(Path.Combine("PhotonPrefabs", nameof(PlayerManager)), Vector3.zero, Quaternion.identity);bine("PhotonPrefabs", "PlayerManager"), Vector3.zero, Quaternion.identity);

为了防止可能的拼写错误,可以使用nameof()

Unity 能够将在Resources文件夹中的文件正常打包

可以在Instantiate的时候为对应GameObject的PV赋值

以创建PlayerController为例,将需要传递的参数封装到最后一个参数object[] 种

1
2
3
4
5
6
7
8
9
10
11
12
13
void CreateController() 
{
Transform spawnpoint = SpawnManager.Instance.GetSpawnpoint();
Debug.Log("[Player]Instantiated Player Controller: " + PhotonNetwork.NickName);
controller = PhotonNetwork.Instantiate(System.IO.Path.Combine("PhotonPrefabs",nameof(PlayerController)),spawnpoint.position, spawnpoint.rotation, 0, new object[] {PV.ViewID});
}


void GetParam()
{
//拆箱时,需要指定cast的类型
int id = (int)PV.InstantiationData[0];
}

Photon Transform View

需要通过Photon同步Transform信息的物体,应当在Photon View之外额外挂在此组件。通过Photon同步时,不应当继续使用本地的物理等等逻辑计算的Transform信息,否则会导致GameObject的抖动等不稳定现象。

在初始化之初关闭即可

1
2
3
4
if(!PV.isMine)
{
Destroy(rb);
}

PUN(Not recommanded)

Photon Unity Networking Classic - FREE | 网络 | Unity Asset Store

Photon.MonoBehaviour vs. Photon.PunBehaviour

For PunBehaviour:

This class provides a .photonView and all callbacks/events that PUN can call. Override the events/methods you want to use.

多与回调相关时候,应当使用 Photon.PunBehaviour

Install Anaconda & Jupyter Notebook

0. Intro

Anaconda offers the easiest way to perform Python/R data science and machine learning on a single machine.

Jupyter Notebook is the original web application for creating and sharing computational documents.

More information related to this topic could be found from:Installing Python · cs109/content Wiki · GitHub

1. Install Anaconda

官网下载链接:Anaconda | Anaconda Distribution

For Windows user:

  1. 点击绿色按钮即可下载,若下载速度过慢,可以考虑镜像清华大学开源软件镜像站 | Tsinghua Open Source Mirror,或右键使用迅雷下载。
    c61a4a5e9ff2e264b7a7a4a4e0289ad9.png

  2. 下载完毕后打开安装包,一路next默认同意即可。安装进度条走完需要约五到六分钟。

  3. 安装成功后,打开开始菜单(win+s)→搜索Anaconda,打开。
    e9559ebb95e08a8d87c1d6b8e5b52ee7.png

  4. 一般首次启动时间较长,有时会提示更新,安装更新即可。

  5. 启动 Jupyter Notebookd31359c5234f7109b9819b59c01f0f46.png

  6. 正常情况下浏览器打开,可以看到所在目录

    77e23a5cd60e6fd6715d0442d68ed9dc.png

  7. .ipynb文件添加到该目录下,点击即可在浏览器中打开。

Install Libraries

a379d50d6d045afbb61b641f41d972d7.png

Change the working directory

如果未生成过 Jupyter 的配置文件jupyter_notebook_config.py
打开 Anaconda Prompt
026c47fcb4a58e006629a22f4fa4fb3c.png
输入

1
jupyter notebook --generate-config

配置文件生成在了对应路径下,即C:\Users\xiaonan\.jupyter

打开此配置文件,找到c.NotebookApp.notebook_dir字段,等号右边填入需要变更为的目标路径
c.NotebookApp.notebook_dir = "D:\XJTLU\INT303\Labs",并去除行前的注释#

重新打开Jupyter Notebook 即可。