PL真有意思(二):程序設計語言語法

前言

雖然標題是程序語言的語法,但是講的是對詞法和語法的解析,其實關於這個前面那個寫編譯器系列的描述會更清楚,有關語言語法的部分應該是穿插在整個設計當中的,也看語言設計者的心情了

和英語漢語這些自然語言不一樣,計算機語言必須是精確的,它們的語法和語義都必須保證沒有歧義,這當然也讓語法分析更加簡單

所以對於編譯器一項很重要的任務就是時別程序設計語言的結構規則,要完成這個目標就需要兩個要求:

  • 完成對語法規則的描述
  • 確定給定程序是否按照這些規則構造起來,也就是符合語法規則

第一個要求主要由正則表達式和上下文無關文法來描述完成,而第二個要求就是由編譯器來完成,也就是語法分析了

描述語法:正則表達式和上下文無關語法

對於詞法,都可以用三種規則描述出來:

  1. 拼接
  2. 選擇
  3. Kleene(也就是重複任意多次)

比如一個整數常量就可以是多個数字重複任意多次,也叫做正則語言。如果對於一個字符串,我們再加入遞歸定義即可以描述整個語法,就可以稱作上下文無關語法

單詞正則表達式

對於程序語言,單詞的類型不外乎關鍵字、標識符、符合和各種類型的常量

對於整數常量就可以用這樣的正則表達式來表示

integer -> digit digit* digit -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

上下文無關文法

一般正則表達式只適用於描述單詞,因為正則表達式無法描述嵌套結構,一般正則表達式的實現都是用有限狀態自動機,之前用Python實現了一個簡單的也是這樣,但是對於匹配任意深度的嵌套結構就需要有一個任意大的狀態機,顯然不符合。而定義嵌套結構對於描述語法非常有用,所以就有了上下文無關文法

expr := id | number | - expr | ( expr ) | expr or expr

op := + | - | * | /

對於上下文無關文法,每條規則叫做一個產生式,產生式左部的符合稱為非終結符,而右部則是多個終結符或者非終結符,最後所有規則都會推到至終結符上,而終結符就是正則表達式定義的單詞

推導和語法樹

一個正確的上下文無關文法,就可以指導我們如何生成一個合乎語法的終結符串

最簡單的就是從開始符號開始,用這個產生式的右部取代開始符合,再從得到的串選擇一個非終結符繼續進行推導,直到沒有剩下的非終結符,這個過程就像遞歸構造一個樹的過程

expr := expr op expr
     := expr op id
     := expr + id
     := expr op expr + id
     := expr op id + id
     := expr * id + id
     := id * id + id

但是對於給定的上下文語法有可能會推導出不止一顆語法分析樹,我們就說這個上下文語法是存在歧義性的。所以對於上面的上下文無關語法還有更好的文法

掃描

掃描也就是詞法分析,詞法分析完全可以不需要什麼正則表達式、自動機什麼的,徒手擼出來,現在業界為了更好的生成錯誤信息,應該很多也是手工的詞法分析器

手工的詞法分析器,無非就是一直讀入字符,到能判斷出它的token在送入語法分析器

有限狀態自動機

使用有限狀態機的詞法分析一般都是這樣的幾個步驟

  • 給出詞法的正則表達式

  • 將正則表達式轉換為非確定有限自動機(NFA)

其實對於任意的正則表達式都可以用拼接、選擇和Kleene閉包來表示

而同樣的,有限自動機也可以通過這三種方式來表示,圖就不畫了,這個在之前寫Python正則表達式引擎的文章里都畫過了(溜了

  • 將NFA轉換為確定性有限狀態自動機(DFA)

將NFA轉換到DFA可以採用的是子集構造法,主要思想就是,在讀入給定輸入之後所到達的DFA狀態,表示的是原來NFA讀入同樣輸入之後可能澳大的所有狀態

  • 最小化DFA

對於最小化DFA的主要思想是,我們把DFA所有狀態分為兩個等價類,終止態狀態和非終止狀態。然後我們就反覆搜索等價類X和輸入符合c,使得當給定C作為輸入時,X的狀態能轉換到位於k>1個不同等價類中的狀態。之後我們就把X劃分為k個類,使得類中所有轉檯對於C都會轉移到同一個老類的成員。直到無法再按這種方式找到劃分的類時,我們就完成了

這四個步驟在之前的寫的正則表達式引擎中都完成了,在那三篇文章里會更詳細一點

語法分析

一般語法分析器的輸入是token流,而輸出是一顆語法分析樹。其中分析方法一般可以分為自上而下和自下而上兩類,這些類中最重要的兩個分別稱為LL和LR

LL表示從左向右,最左推導,LR表示從左向右,最右推導。這兩類文法都是從左到右的順序讀取輸入,然後語法分析器試圖找出輸入的推導結果

自上而下的方式

一般自上而下的語法分析器比較符合之前的推導方法,從根節點開始像恭弘=叶 恭弘節點反覆的遞歸推導,直到當前的恭弘=叶 恭弘節點都是終結符

  • 遞歸下降

遞歸下降很符合上面說的從根節點出發進行推導,一般用於一些相對簡單一些的語言

read A
read B
sum := A + B
write sum
write sum / 2

比如對於這個程序的遞歸下降,語法分析器一開始調用program函數,在讀入第一個單詞read后,program將調用stmt_list,再接着調用stmt才真正開始匹配read A。以這種方式繼續下去,語法分析器執行路徑將追溯出語法分析樹的從左向右、自上而下的遍歷

  • 表格驅動的LL自上而下

表格驅動的LL是基於一個語法分析表格和一個棧

分析流程是

  1. 初始化一個棧
  2. 將開始符號壓入棧
  3. 彈出棧頂,然後根據棧頂的符號和當前的輸入符號查表
  4. 如果彈出的是非終結符,將會繼續查表來確定下一個壓入棧中的產生式
  5. 如果是終結符將進行匹配

預測集合

從上面可以看出來最重要的就是那個語法分析表格了,語法分析表格其實就是根據當前輸入字符對下一個產生式的預測,這裏就要用到一個概念:預測集合,也就是First和Follow集合。這個在之前寫編譯器系列講的比較詳細,在這裏就不寫了

當然LL語法也會有很多處理不了的文法,所以也才會有其它的語法分析方法

自下而上的方式

在實踐中,自下而上的語法分析都是表格驅動的,這種分析器在一個棧中保存所有部分完成的子樹的根。當它從掃描器中得到一個新的單詞時,就會將這個單詞移入棧。當它發現位於棧頂的若干符號組成一個右部時,它就會將這些符號歸約到對應的左部。

一個自底向上的語法分析過程對應為一個輸入串構造語法分析書的過程,它從恭弘=叶 恭弘子節點開始,通過shift和reduce操作逐漸向上到達根節點

自底向上的語法分析需要一個堆棧來存放解析的符號,例如對於如下語法:

0.  statement -> expr
1.  expr -> expr + factor
2.           | factor
3.  factor ->  ( expr )
4.           | NUM

來解析1+2

stack input
null 1 + 2
NUM + 2 開始讀入一個字符,並把對應的token放入解析堆棧,稱為shift操作
factor + 2 根據語法推導式,factor -> NUM,將NUM出棧,factor入棧,這個操作稱為reduce
expr + 2 這裏繼續做reduce操作,但是由於語法推導式有兩個產生式,所以需要向前看一個符合才能判斷是進行shift還是reduce,也就是語法解析的LA
expr + 2 shift操作
expr + NUM null shift操作
expr + factor null 根據fator的產生式進行reduce
expr null reduce操作
statement null reduce操作

此時規約到開始符號,並且輸入串也為空,代表語法解析成功

有限狀態自動機的構建

0.  s -> e
1.  e -> e + t
2.  e -> t
3.  t -> t * f
4.  t -> f
5.  f -> ( e )
6.  f -> NUM
  • 對起始推導式做閉包操作

先在起始產生式->右邊加上一個.

s -> .e

對.右邊的符號做閉包操作,也就是說如果 . 右邊的符號是一個非終結符,那麼肯定有某個表達式,->左邊是該非終結符,把這些表達式添加進來

s -> . e
e -> . e + t
e -> . t

對新添加進來的推導式反覆重複這個操作,直到所有推導式->右邊是非終結符的那個所在推導式都引入

  • 對引入的產生式進行分區

把 . 右邊擁有相同非終結符的表達式划入一個分區,比如

e -> t .
t -> t . * f

就作為同一個分區。最後把每個分區中的表達式中的 . 右移動一位,形成新的狀態節點

  • 對所有分區節點構建跳轉關係

根據每個節點 . 左邊的符號來判斷輸入什麼字符來跳入該節點

比如, . 左邊的符號是 t, 所以當狀態機處於狀態0時,輸入時 t 時, 跳轉到狀態1。

  • 對所有新生成的節點重複構建

最後對每個新生成的節點進行重複的構建,直到完成所有所有的狀態節點的構建和跳轉

小結

這一篇主要是提了對詞法和語法的分析過程,因為想要結合語言設計和實踐,更詳細的應該去看前面的寫一個編譯器系列

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

特斯拉需求大!住友金屬追加擴產電池材料、增至2.5倍

 

日本住友金屬礦山(Sumitomo Metal Mining)28日發布新聞稿宣布,該公司雖已於去年10月27日表示將砸下180億日圓於2018年1月將鋰離子電池正極材料「鎳酸鋰(見附圖)」月產能擴增至3,550噸,不過因電動車(EV)用鋰離子電池需求擴大,因此決議對「鎳酸鋰」進行追加增產措施,計畫投入40億日圓於磯浦工廠進行增產工程,目標在2018年6月將整體「鎳酸鋰」月產能擴增至4,550噸、將達現行的2.5倍。

住友金屬礦山指出,該公司正持續擴大與Panasonic攜手研發的高性能鎳酸鋰產能,此次為了因應Panasonic擴大鋰離子電池產能、故決定對鎳酸鋰進行追加增產投資。

據日經新聞指出,住友金屬礦山追加增產「鎳酸鋰」主要是因應美國EV廠特斯拉(Tesla)增產所需。據報導,住友金屬礦山目前透過Panasonic供應特斯拉電動車所需的大部分車用電池正極材料。

根據嘉實XQ全球贏家系統報價,截至台北時間31日上午8點50分為止,住友金屬礦山上揚1.34%至1,660.5日圓,稍早最高漲至1,664.5日圓創約5個月來(2月16日以來)新高水準。

特斯拉平價電動車「Model 3」於7月28日正式交車。Business Insider、The Motley Fool、Electrek等外電報導,Model 3售價35,000美元,特斯拉原本計畫要在2020年底前,將Model 3年產量提升至50萬台,但去(2016)年該公司把目標提前兩年、移至2018年。不過,Model 3目前的生產年率還只有10萬台,特斯拉想要達標、產速勢必得快速拉升。

富士經濟6月22日公布調查報告指出,預估2030年時EV年銷售量將增至407萬台、超越HV(油電混合車、2030年銷售量預估為391萬台),且之後雙方的差距將持續擴大。富士經濟預估,在中國需求增加加持下,2035年EV全球銷售量將擴大至630萬台、將達2016年的13.4倍(較2016年增加12.4倍)。

(本文內容由授權使用。圖片出處:MoneyDJ)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

EV續航距離倍增、媲美汽油車!GS傳量產新電池

 

日經新聞8日報導,GS Yuasa將在2020年開始量產可讓電動車(EV)充飽一次電所能行駛的距離擴增至現行2倍的新型鋰離子電池。現行部分EV充飽電所能行駛的距離僅有汽油車的一半左右水準,而搭載GS新型電池將讓EV續航距離增加至可媲美汽油車的水準。GS為全球第4大車用鋰離子電池廠。

報導指出,上述新型電池由GS、三菱商事(Mitsubishi)、三菱汽車(Mitsubishi Motors)合資設立的電池製造公司「Lithium Energy Japan(簡稱LEJ)」所研發,將在2020年透過LEJ工廠進行量產,將供應給日系、歐系車廠使用,價格目標是壓低至同於現行產品的水準。GS、三菱商事、三菱汽車對LEJ的出資比重分別為51%、46%、3%。

根據GS公布的財報資料顯示,上季(2017年4-6月)GS車用鋰離子電池事業營收較去年同期成長4.2%至87.24億日圓、營損額自去年同期的2.17億日圓縮小至1.64億日圓。

法國、英國已紛紛表明計畫在2040年停售汽柴油車,提振EV後續需求料將急速擴大。

富士經濟6月22日公布調查報告指出,預估2030年時EV年銷售量將增至407萬台、超越HV(油電混合車、2030年銷售量預估為391萬台),且之後雙方的差距將持續擴大。富士經濟預估,在中國需求增加加持下,2035年EV全球銷售量將擴大至630萬台、將達2016年的13.4倍(較2016年增加12.4倍)。

(本文內容由授權使用。圖片出處:GS Yuasa)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

中國30餘省市加大政策補貼力道,充電樁建設步爆發期

21經濟網報導,為推進新能源汽車行業健康發展,中國許多城市近期紛紛調整補貼政策,下調整車補貼,上調充電樁補貼。中國國家新能源汽車技術創新工程專家組組長王秉剛表示,現在電動車這條腿已走在前頭,要拉動基礎設施建設這另一條腿。從建設落後到「適度超前」,業內人士預計,未來三年中國充電樁產業將迎來大發展,市場規模預估達千億級(人民幣,下同)。

根據中國深圳財委和深圳發改委日前發佈《深圳市2017年新能源汽車推廣應用財政支持政策》通知,汽車補貼大幅下調。而在此同時,充電樁的補貼則較去年高一倍。其中,新建直流充電設備的補貼標準從原有的300元/千瓦提升到600元/千瓦,交流充電設備補貼從150元/千瓦提升到300元/千瓦。

另據中國國家能源局和中國電動汽車充電基礎設施促進聯盟聯合編制的《中國電動汽車充電基礎設施發展年度報告(2016-2017)》顯示,包括北京、唐山、貴陽、廈門、石家莊等30多個省市均出臺了充電設施建設補貼政策,加大了補貼力道,補貼最高達設施投資的30%。

據充電樁數量最多的北京在8月8日公佈資料,截至今年6月底,累計建成約9.23萬個充電樁,形成六環內平均服務半徑5公里的公用充電網路,且充電樁網路下半年繼續擴建。根據目標,北京將在2020年前建電動汽車充電樁約43.5萬個;其中,今年北京計畫新建3,000個公用充電樁,並研擬對新建建築配套停車場的充電樁數量進行固定配比。

北京將在未來數年迎來充電樁數量大爆發。而事實上,除了北京之外,多地對於充電樁建設的積極性都頗高,例如到2020年,上海市新能源車輛充電樁規模至少達到21.1萬個;廣東將建成分散式充電樁約35萬個。

根據中國《能源發展十三五規劃》,到2020年將建設「四縱四橫」城際電動汽車快速充電網路,新增超過800座城際快速充電站;新增集中式充換電站超過1.2萬座,分散式充電樁則超過480萬個,滿足全國500萬輛電動汽車充換電需求。

不過,政策補貼力道增大的同時,也不斷有建成充電樁使用率低、充電位被燃油汽車佔用等現象發生,由此產生了充電樁「白補貼」的質疑;特別是有了新能源汽車騙補的前車之鑒,對充電樁補貼發放標準從嚴考核的呼聲越來越大。對此,王秉剛建議,應加強對補貼資金發放、使用等的管理和監督並明確制度,應不能建成就能拿補貼,而是有效運行後才能拿補貼。

(本文內容由授權使用。圖片出處:public domain CC0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

搶當環保車二哥!現代推新型燃料電池車,續航增40%

韓聯社報導,南韓汽車業龍頭現代汽車(Hyundai Motor)17日在首爾公開了新型燃料電池車(FCV),並預計於2018年初開賣。和現代汽車現行FCV車款「Tucson」相比,新型FCV的氫燃料電池系統的效率、性能、耐久性、儲存容量都有劃時代的提升,充填一次燃料(氫)所能行駛的距離可較現行的415km大幅提升約40%至580km,且預定將搭載先進駕駛輔助系統(Advanced Driver Assistance Systems;ADAS)。   現代汽車並宣布,計畫在2020年結束前將環保車(Eco-car)車款數量自現行的14款倍增至31款,其中油電混合車(HV)為10款、插電式油電混合車(PHV)11款、電動車(EV)8款、FCV 2款,目標是在全球環保車市場上成為僅次於日本豐田(Toyota)的第2大廠。   日經新聞報導,現代汽車上述新型FCV預計於2018年Q1搶先在南韓市場開賣,而現代汽車幹部指出,「預計將在2018年下半年搶進歐美及澳洲市場,且也計畫在中國市場進行販售」,期望藉此在FCV市場上追擊跑在前頭的豐田。現代汽車上述新型FCV馬達輸出為163馬力、高於豐田「MIRAI」的154馬力。   報導並指出,現代汽車也宣布,計畫在2018年上半年開賣充飽一次電可行駛390km的SUV款EV,且現代汽車也正著手研發續航距離可達500km的EV。   豐田汽車社長豐田章男2016年1月接受日經/金融時報聯訪時表示,「今後100年將是環保車的時代」,他說,「汽車產業過去1個世紀來,主要以汽油引擎為主流,不過下一個100年,燃料電池車、插電式油電混合車等各種環保車需求將增長」。   富士經濟6月22日公布調查報告指出,現行環保車種以HV需求最大,預估截至2025年左右為止,HV仍將是環保車種的主流產品,不過PHV、EV在2025年以後需求將急速增加,預估2030年時EV年銷售量將增至407萬台、超越HV(2030年銷售量預估為391萬台),且之後雙方的差距將持續擴大。   富士經濟預估,在中國需求增加加持下,2035年EV全球銷售量將擴大至630萬台、將達2016年的13.4倍(較2016年增加12.4倍)。   (本文內容由授權使用)  

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

第二屆上海-斯圖加特汽車及動力技術國際研討會2017

第二屆上海斯圖加特汽車及動力技術國際研討會2017

2017年10月26日-27日/ 中國·上海

 

在上海市及政府斯圖加特市政府的指導下,利用同濟大學和斯圖加特大學各自的學科優勢和研發成果,“2017上海-斯圖加特汽車及動力技術國際研討會”將於2017年10月26日-27日在上海召開。

 

大會以“汽車輕量化、智慧網聯、綠色汽車”為主題,將研討汽車整車設計和輕量化、智慧底盤與電驅動、環境感知技術、氫能與燃料電池、車用電源技術等,屆時全球400多位行業專家將集聚一堂,共同探討新能源汽車產業的前沿發展,共同建立聯繫,促進合作!

 

參會者可以以論文投稿的形式參與此次會議,論文審核組委會將負責從所有參會者所投稿件中遴選出品質較高的優秀學術論文,在會議當天進行學術性報告演講,並在同濟大學學報增刊(100%EI期刊檢索)上進行發表。

 

大會亮點:

3大論壇涉及9個主題內容專業豐富的會議

40篇論文,100%EI檢索

45位海內外權威發言嘉賓

400+知名行業人士蒞臨

 

我們期待與貴單位一起出席於2017年10月26日-27日在中國上海舉辦的上海-斯圖加特汽車及動力技術國際研討會,以利決策!

 

更多會議資訊,請登錄會議官方網站:http://www.sssaet.com/index_cn.htm

連絡人:Latika LIU(劉小姐)

電話:021-6093 0815

傳真:021-6047 5887

郵箱:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

Asp.net Core 系列之–5.認證、授權與自定義權限的實現

ChuanGoing 2019-11-24

  asp.net core系列已經來到了第五篇,通過之前的基礎介紹,我們了解了整個流程,實現,並且簡單的介紹了實現,結合上一篇的和本篇將要介紹的權限,大致的可以形成一個簡單的後端系統架構。當然這些都是零散的一些技術概念的介紹,後面如果有時間的話,我想詳細的介紹下如何利用領域驅動來實現一個實際案例。

話不多講,下面來看下本篇的學習曲線:

1.認識Identityserver4

2.Identityserver4實現認證與授權

3.自定義權限的實現

認識Identityserver4

  關於Identityserver4(ids4)的概念介紹,請查看一文。我這裏要說的是,asp.net core 下的ids4集成了認證與授權兩大功能,使得我們非常方便的實現一個開放的認證與授權平台,比如公司內部多個系統的集成登錄(單點登錄)/第三方系統數據共享/統一的認證中心等。整個業務流程大致為:

1.用戶首先的有用戶中心的賬號信息,因此需要註冊一個賬號

2.用戶訪問某個站點應用,需要去到用戶中心認證

3.認證通過,用戶得到其在用戶中心註冊的相應信息及其權限時限、範圍、大小

4.認證不通過,即非法用戶,提示用戶註冊

5.在第3步的前提下,若用戶訪問到另一個站點(採用同一認證平台),這時用戶可以用之前認證通過後拿到的訪問令牌訪問此站點,若此令牌中包含此站點的相應權限即可之前登錄。

Identityserver4實現認證與授權

首先,新建一個asp.net core web 空項目,並且添加如下IdentityServer4 Nuget包

在ConfigureServices添加如下代碼

註冊IdentityServer中間件,如下5個配置分別表示:

1.AddDeveloperSigningCredential:開發模式下的簽名證書,開發環境啟用即可

2.AddInMemoryApiResources:相關資源配置

public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("WebApi", "ChuanGoingWebApi"),
                new ApiResource("ProductApi", "ChuanGoingWebProduct")
            };
        }

GetApiResources

這裏配置了兩個Api資源

3.AddInMemoryIdentityResources:OpenID Connect相關認證信息配置

 public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile()
            };
        }

GetIdentityResources

4.AddInMemoryClients:客戶端信息配置

 public static IEnumerable<Client> GetClients(IConfiguration Configuration)
        {
            var OnlineConfig = Configuration.GetSection("OnlineClient");
            var List = new List<Client>
            {
                new Client()
                {
                    ClientId = "ClientCredentials",
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    ClientSecrets = { new Secret("ClientSecret".Sha256()) },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "WebApi",
                        "ProductApi"
                    },
                    AccessTokenLifetime = 10 * 60 * 1
                },

                new Client()
                {
                    ClientId = "ResourceOwnerPassword",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                    ClientSecrets = { new Secret("ClientSecret".Sha256()) },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "WebApi",
                        "ProductApi"
                    },
                    AccessTokenLifetime = 10 * 60 * 1
                },
                  /*
                  隱式模式:https://localhost:6005/connect/authorize?client_id=Implicit&redirect_uri=http://localhost:5000/Home&response_type=token&scope=WebApi
                  */
                new Client()
                {
                    ClientId = "Implicit",
                    ClientName = "ImplicitClient",
                    AllowedGrantTypes = GrantTypes.Implicit,
                    ClientSecrets = { new Secret("ImplicitSecret".Sha256()) },
                    RedirectUris ={OnlineConfig.GetValue<string>("RedirectUris") },
                    PostLogoutRedirectUris = {OnlineConfig.GetValue<string>("LogoutRedirectUris") },
                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "WebApi",
                        "ProductApi"
                    },
                    AccessTokenLifetime = 10 * 60 * 1,
                    //允許將token通過瀏覽器傳遞
                     AllowAccessTokensViaBrowser=true
                },
                /*
                 * 授權碼模式:https://localhost:6005/connect/authorize?client_id=GrantCode&redirect_uri=http://localhost:5000/Home&response_type=code&scope=WebApi
                 */
                new Client()
                {
                   //客戶端Id
                    ClientId="GrantCode",
                    ClientName="GrantCodeClient",
                    //客戶端密碼
                    ClientSecrets={new Secret("CodeSecret".Sha256()) },
                    //客戶端授權類型,Code:授權碼模式
                    AllowedGrantTypes=GrantTypes.Code,
                    //允許登錄后重定向的地址列表,可以有多個
                     RedirectUris ={OnlineConfig.GetValue<string>("RedirectUris") }, 
                    //允許訪問的資源
                    AllowedScopes={
                        "WebApi",
                        "ProductApi"
                    }
                }
            };
            return List;
        }

GetClients

分別對象Auth2.0的四種模式,本篇將用到的是ResourceOwnerPassword模式,其他幾種可在篇尾github鏈接查看源碼的實現

5.AddTestUsers:用戶配置,可結合緩存/持久化

public static List<TestUser> GetUsers()
        {
            return new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = Guid.NewGuid().ToString(),
                    Username = "admin",
                    Password = "123456"

                    //Claims = new List<Claim>
                    //{
                    //    new Claim("name", "admin"),
                    //    new Claim("website", "https://www.cnblogs.com/chuangoing")
                    //}
                },
                new TestUser
                {
                    SubjectId = Guid.NewGuid().ToString(),
                    Username = "chuangoing",
                    Password = "123456"

                    //Claims = new List<Claim>
                    //{
                    //    new Claim("name", "chuangoing"),
                    //    new Claim("website", "https://github.com/chuangoing")
                    //}
                }
            };
        }

GetUsers

 定義兩個測試用戶,注意這裏的SubjectId,用作用戶中心註冊的openid(認證唯一),後面將會用到

然後,Configure中添加app.UseIdentityServer();//啟用ids4

至此,ids4 服務完成

用postman測試下:

 

返回jwt accesstoken:

 

 

將token內容解碼,如下:

 

 可以看到,裡面包含我們配置的ProductApi/WebApi的權限

將token信息加入到http的header中:

 

  注意Bearer後面有個空格,訪問order的獲取訂單信息:

 

 

 自定義權限的實現

  這裏,我們將api中的action分別定義一個權限代碼,用戶擁有了此action訪問權限(擁有此權限代碼)即可訪問,簡單實現如下:

1.定義權限特性標識,api的action指定某個標識

public class PermissionAttribute : Attribute
    {
        /// <summary>
        /// 權限代碼
        /// </summary>
        public string Code { get; }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="code">權限代碼</param>
        public PermissionAttribute(string code)
        {
            Code = code;
        }
    }

PermissionAttribute

 

 此處,get action定義了訪問權限標識為”XYZ”

同樣,我們這裏需要用到一個權限過濾器,利用過濾器的Aop實現權限過濾業務處理:

 public class PermissionFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var user = context.HttpContext.User;
            if (user.Identity.IsAuthenticated)
            {
                //TODO:用戶自定義權限驗證
                Guid userId = context.HttpContext.GetId();
                bool right;
                #region 自定義權限驗證
                //根據userId判斷用戶內部系統權限信息

                //var userPermissions = repo.GetUserPermissions(userId);
                //var permissions = repo.GetPermissions();
                var metas = context.ActionDescriptor.EndpointMetadata;
                foreach (var meta in metas)
                {
                    if (meta is PermissionAttribute permission)
                    {
                        //if (!permissions.Any(p => permission.Code.Any(c => c == p.Code))
                        //    && !userPermissions.Any(p => permission.Code.Any(c => c == p.Code)))
                        //{
                        //    throw new WebException(HttpStatusCode.Forbidden, MessageCodes.AccessDenied, "你沒有訪問該資源的權限");
                        //}
                        //break;
                    }
                }

                right = false;
                #endregion
                if (!right)
                {
                    context.Result = new ContentResult() { StatusCode = (int)HttpStatusCode.Forbidden, Content = "你沒有訪問該資源的權限" };
                }

            }
        }

PermissionFilter

同時,啟用權限過濾器配置

 

 

部分代碼略過,詳細的請查看篇尾的源碼鏈接

利用第二節的認證授權得到的token,我們用postman測試下:

 

過濾器切面成功工作

 

 

 還記得第一節說的SubjectId么?這裏利用這個openid,去內部系統去匹配相關用戶信息,相關業務就不深入了,有興趣的朋友可以下載示例完善下

 

 至此,整個權限認證、授權、自定義權限介紹完。

WebApi詳細代碼在Github的 的Domain分支可以找到,AuthServer詳細代碼在中。

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

Tensorflow基礎

說明:本文實例使用Python版本為3.5.6,Tensorflow版本為2.0

介紹

Tensorflow是Google推出的機器學習開源神器,對Python有着良好的語言支持,支持CPU,GPU和Google TPU等硬件,並且已經擁有了各種各樣的模型和算法。目前,Tensorflow已被廣泛應用於文本處理,語音識別和圖像識別等多項機器學習和深度學習領域。

基礎框架

分為三層:應用層、接口層和核心層

應用層

提供了機器學習相關的訓練庫、預測庫和針對Python、C++和Java等變成語言的編程環境,類似於web系統的前端,主要實現了對計算圖的構造。

接口層

對Tensorflow功能模塊的封裝,便於其它語言平台的調用。

核心層

最重要的部分,包括設備層、網絡層、數據操作層和圖計算層,執行應用層的計算。

1.設備層

包括Tensorflow在不同硬件設備上的實現,主要支持CPU、GPU和Mobile等設備,在不同硬件設備上實現計算命令的轉換,給上層提供統一的接口,實現程序的跨平台功能。

2.網絡層

網絡層主要包括RPC和RDMA通信協議,實現不同設備之間的數據傳輸和更新,這些協議都會在分佈式計算中用到。

3.數據操作層

以tensor為處理對象,實現tensor的各種操作和計算。

4.圖計算層

包括分佈式計算圖和本地計算圖的實現,實現圖的創建、編譯、優化和執行等。

設計理念

可以將Tensorflow理解為一張計算圖中“張量的流動”,其中,Tensor(張量)代表了計算圖中的邊,Flow(流動)代表了計算圖中節點所做的操作而形成的數據流動。

其設計理念是以數據流為核心,當構建相應的機器學習模型后,使用訓練數據在模型中進行數據流動,同時將結果以反向傳播的方式反饋給模型中的參數,以進行調參,使用調整后的參數對訓練數據再次進行迭代計算。

編程特點

有兩個編程特點:

  1. 圖的定義和圖的運行完全分開

在tensorflow中,需要預先定義各種變量,建立相關的數據流圖,在數據流圖中創建各種變量之間的計算關係,完成圖的定義,需要把運算的輸入數據放進去后,才會形成輸出值。

  1. 圖的計算在會話中執行

tensorflow的相關計算在圖中進行定義,而圖的具體運行壞境在會話(session)中,開啟會話后,才能開始計算,關閉會話就不能再進行計算了。

舉個例子:

import tensorflow as tf
tf.compat.v1.disable_eager_execution()


a = 3
b = 4
c = 5
y = tf.add(a*b, c)
print(y)

a = tf.constant(3, tf.int32)
b = tf.constant(4, tf.int32)
c = tf.constant(5, tf.int32)
y = tf.add(a*b, c)
print(y)
session = tf.compat.v1.Session()
print(session.run(y))
session.close()

可以看出,在圖創建后,並在會話中執行數據計算,最終輸出結果。

設計的好處就是:學習的過程中,消耗最多的是對數據的訓練,這樣設計的話,當進行計算時,圖已經確定,計算就只剩下一個不斷迭代的過程。

基本概念

Tensor

張量,是tensorflow中最主要的數據結構,張量用於在計算圖中進行數據傳遞,創建了張量后,需要將其賦值給一個變量或佔位符,之後才會將該張量添加到計算圖中。

session

會話,是Tensorflow中計算圖的具體執行者,與圖進行實際的交互。一個會話中可以有多個圖,會話的主要目的是將訓練數據添加到圖中進行計算,也可以修改圖的結構。

調用模式推薦使用with語句:

with session:
    session.run()

Variable

變量,表示圖中的各個計算參數,通過調整這些變量的狀態來優化機器學習算法。創建變量應使用tf.Variable(),通過輸入一個張量,返回一個變量,變量聲明后需進行初始化才能使用。

舉例說明:

import tensorflow as tf
tf.compat.v1.disable_eager_execution()

tensor = tf.ones([1, 3])
test_var = tf.Variable(tensor)
# 初始化變量
init_op = tf.compat.v1.global_variables_initializer()
session = tf.compat.v1.Session()
with session:
    print("tensor is ", session.run(tensor))
    # print("test_var is ", session.run(test_var))
    session.run(init_op)
    print("after init, test_var is", session.run(test_var))

Placeholder

佔位符,用於表示輸入輸出數據的格式,聲明了數據位置,允許傳入指定類型和形狀的數據,通過會話中的feed_dict參數獲取數據,在計算圖運行時使用獲取的數據進行計算,計算完畢后獲取的數據就會消失。

舉例說明:

x = tf.compat.v1.placeholder(tf.int32)
y = tf.compat.v1.placeholder(tf.int32)
z = tf.add(x, y)
session = tf.compat.v1.Session()
with session:
    print(session.run([z], feed_dict={x: [1, 2], y: [2, 3]}))

Operation

操作,是圖中的節點,輸入輸出都是Tensor,作用是完成各種操作,包括:

  1. 數學運算:add, sub, mul, div, exp …
  2. 數組運算:concat, slice, split, rank …
  3. 矩陣運算:matmul, matrixinverse …
  4. 神經網絡構建:softmax, sigmoid, relu …
  5. 檢查點:save, restore …
  6. 隊列和同步:enqueue, dequeue, mutexacquire, mutexrelease …
  7. 張量控制:merge, switch, enter, leave …

Queue

隊列,圖中有狀態的節點。包含入列(endqueue)和出列(dequeue)兩個操作,入列返回計算圖中的一個操作節點,出列返回一個tensor值。

其中,隊列有兩種:

1. FIFOQueue

按入列順序出列的隊列,在需要讀入的訓練樣本有序時使用。舉個例子:

fifo_queue = tf.compat.v1.FIFOQueue(10, 'int32')
init = fifo_queue.enqueue_many(([1, 2, 3, 4, 5, 6], ))
with tf.compat.v1.Session() as session:
    session.run(init)
    queue_size = session.run(fifo_queue.size())
    for item in range(queue_size):
        print('fifo_queue', session.run(fifo_queue.dequeue()))

2. RandomShuffleQueue

以隨機順序出列的隊列,讀入的訓練樣本無序時使用。舉個例子:

rs_queue = tf.compat.v1.RandomShuffleQueue(capacity=5, min_after_dequeue=0, dtypes='int32')
init = rs_queue.enqueue_many(([1, 2, 3, 4, 5], ))
with tf.compat.v1.Session() as session:
    session.run(init)
    queue_size = session.run(rs_queue.size())
    for i in range(queue_size):
        print('rs_queue', session.run(rs_queue.dequeue()))

代碼參考:

以上。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

中國工信部:將制定停止產銷傳統能源汽車的時間表

新華社9日報導,中國工信部副部長辛國斌表示,一些國家已經制定了停止生產銷售傳統能源汽車的時間表。他說,目前工信部也啟動了相關研究、將會同相關部門制定中國的時間表。

報導指出,從現在到2025年將是汽車產業變革最為劇烈的幾年,傳統汽車節能減排要求越來越高,新能源汽車發展加快的同時對技術要求也越來越高,智能聯網將對整個產業巨大影響。辛國斌及專家建議中國車企應深刻認識這種趨勢、及時調整策略。

Thomson Reuters上個月底引述消息人士報導,根據最新提案,明年底中國境內車商8%銷售必須是電動車或油電混合車種、2019年升至10%、2020年升至12%。報導指出,這項規定預計將自2019年起開始落實執行、較原先規劃晚一年。

英國跟隨法國以及馬德里、墨西哥城和雅典等城市的抗空汙腳步,7月宣布將自2040年起禁止販售汽油和柴油新車。英國最大汽車製造商Jaguar Land Rover(JLR)9月7日宣布,2020年起旗下所有新車都將具備電動或油電混合驅動選項。德國車廠BMW也宣布將自2020年起開始量產電動車、預估到2025年將有12種純電動車款。

BBC News 10日報導,上述最新消息將對中國石油需求帶來連鎖效應。中國目前是全球第二大石油消費國。依據目前的規劃,中國希望在2025年將電動車/油電混合車銷售佔比至少拉升至五分之一。

根據DNV GL首度發布的「能源轉型展望」報告,受電動車滲透率持續上揚的影響,石油供應將在2020-2028年期間轉趨持平、隨後大幅下降,2034年將遭天然氣超越。

這份報告預估電動車、內燃引擎車將在2022年達到「成本平價」,預估到2033年全球半數輕型新車銷售量都將是電動車。

(本文內容由授權使用。圖片出處:public domain CC0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

three.js使用gpu選取物體並計算交點位置

光線投射法

使用three.js自帶的光線投射器(Raycaster)選取物體非常簡單,代碼如下所示:

var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();

function onMouseMove(event) {
    // 計算鼠標所在位置的設備坐標
    // 三個坐標分量都是-1到1
    mouse.x = event.clientX / window.innerWidth * 2 - 1;
    mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
}

function pick() {
    // 使用相機和鼠標位置更新選取光線
    raycaster.setFromCamera(mouse, camera);

    // 計算與選取光線相交的物體
    var intersects = raycaster.intersectObjects(scene.children);
}

它是採用包圍盒過濾,計算投射光線與每個三角面元是否相交實現的。

但是,當模型非常大,比如說有40萬個面,通過遍歷的方法選取物體和計算碰撞點位置將非常慢,用戶體驗不好。

但是使用gpu選取物體不存在這個問題。無論場景和模型有多大,都可以在一幀內獲取到鼠標所在點的物體和交點的位置。

使用GPU選取物體

實現方法很簡單:

1.  創建選取材質,將場景中的每個模型的材質替換成不同的顏色。

2. 讀取鼠標位置像素顏色,根據顏色判斷鼠標位置的物體。

具體實現代碼:

1. 創建選取材質,遍歷場景,將場景中每個模型替換為不同的顏色。

let maxHexColor = 1;

// 更換選取材質
scene.traverseVisible(n => {
    if (!(n instanceof THREE.Mesh)) {
        return;
    }
    n.oldMaterial = n.material;
    if (n.pickMaterial) { // 已經創建過選取材質了
        n.material = n.pickMaterial;
        return;
    }
    let material = new THREE.ShaderMaterial({
        vertexShader: PickVertexShader,
        fragmentShader: PickFragmentShader,
        uniforms: {
            pickColor: {
                value: new THREE.Color(maxHexColor)
            }
        }
    });
    n.pickColor = maxHexColor;
    maxHexColor++;
    n.material = n.pickMaterial = material;
});

 

PickVertexShader:

void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

 

PickFragmentShader:

uniform vec3 pickColor;

void main() {
    gl_FragColor = vec4(pickColor, 1.0);
}

 

2.  將場景繪製在WebGLRenderTarget上,讀取鼠標所在位置的顏色,判斷選取的物體。

let renderTarget = new THREE.WebGLRenderTarget(width, height);
let pixel = new Uint8Array(4);

// 繪製並讀取像素
renderer.setRenderTarget(renderTarget);
renderer.clear();
renderer.render(scene, camera);
renderer.readRenderTargetPixels(renderTarget, offsetX, height - offsetY, 1, 1, pixel); // 讀取鼠標所在位置顏色

// 還原原來材質,並獲取選中物體
const currentColor = pixel[0] * 0xffff + pixel[1] * 0xff + pixel[2];

let selected = null;

scene.traverseVisible(n => {
    if (!(n instanceof THREE.Mesh)) {
        return;
    }
    if (n.pickMaterial && n.pickColor === currentColor) { // 顏色相同
        selected = n; // 鼠標所在位置的物體
    }
    if (n.oldMaterial) {
        n.material = n.oldMaterial;
        delete n.oldMaterial;
    }
});

說明:offsetX和offsetY是鼠標位置,height是畫布高度。readRenderTargetPixels一行的含義是選取鼠標所在位置(offsetX, height – offsetY),寬度為1,高度為1的像素的顏色。

pixel是Uint8Array(4),分別保存rgba顏色的四個通道,每個通道取值範圍是0~255。

完整實現代碼:

使用GPU獲取交點位置

實現方法也很簡單:

1. 創建深度着色器材質,將場景深度渲染到WebGLRenderTarget上。

2. 計算鼠標所在位置的深度,根據鼠標位置和深度計算交點位置。

具體實現代碼:

1. 創建深度着色器材質,將深度信息以一定的方式編碼,渲染到WebGLRenderTarget上。

深度材質:

const depthMaterial = new THREE.ShaderMaterial({
    vertexShader: DepthVertexShader,
    fragmentShader: DepthFragmentShader,
    uniforms: {
        far: {
            value: camera.far
        }
    }
});

DepthVertexShader:

precision highp float;

uniform float far;

varying float depth;

void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    depth = gl_Position.z / far;
}

DepthFragmentShader:

precision highp float;

varying float depth;

void main() {
    float hex = abs(depth) * 16777215.0; // 0xffffff

    float r = floor(hex / 65535.0);
    float g = floor((hex - r * 65535.0) / 255.0);
    float b = floor(hex - r * 65535.0 - g * 255.0);
    float a = sign(depth) >= 0.0 ? 1.0 : 0.0; // depth大於等於0,為1.0;小於0,為0.0。

    gl_FragColor = vec4(r / 255.0, g / 255.0, b / 255.0, a);
}

重要說明:

a. gl_Position.z是相機空間中的深度,是線性的,範圍從cameraNear到cameraFar。可以直接使用着色器varying變量進行插值。

b. gl_Position.z / far的原因是,將值轉換到0~1範圍內,便於作為顏色輸出。

c. 不能使用屏幕空間中的深度,透視投影后,深度變為-1~1,大部分非常接近1(0.9多),不是線性的,幾乎不變,輸出的顏色幾乎不變,非常不準確。

d. 在片元着色器中獲取深度方法:相機空間深度為
gl_FragCoord.z,屏幕空間深度為
gl_FragCoord.z /  gl_FragCoord.w

e. 上述描述都是針對透視投影,正投影中gl_Position.w為1,使用相機空間和屏幕空間深度都是一樣的。

f. 為了盡可能準確輸出深度,採用rgb三個分量輸出深度。gl_Position.z/far範圍在0~1,乘以0xffffff,轉換為一個rgb顏色值,r分量1表示65535,g分量1表示255,b分量1表示1。

 

完整實現代碼:

 

2. 讀取鼠標所在位置的顏色,將讀取到的顏色值還原為相機空間深度值。

a. 將“加密”處理后的深度繪製在WebGLRenderTarget上。讀取顏色方法

let renderTarget = new THREE.WebGLRenderTarget(width, height);
let pixel = new Uint8Array(4);

scene.overrideMaterial = this.depthMaterial;

renderer.setRenderTarget(renderTarget);

renderer.clear();
renderer.render(scene, camera);
renderer.readRenderTargetPixels(renderTarget, offsetX, height - offsetY, 1, 1, pixel);

說明:offsetX和offsetY是鼠標位置,height是畫布高度。readRenderTargetPixels一行的含義是選取鼠標所在位置(offsetX, height – offsetY),寬度為1,高度為1的像素的顏色。

pixel是Uint8Array(4),分別保存rgba顏色的四個通道,每個通道取值範圍是0~255。

 

b. 將“加密”后的相機空間深度值“解密”,得到正確的相機空間深度值。

if (pixel[2] !== 0 || pixel[1] !== 0 || pixel[0] !== 0) {
    let hex = (this.pixel[0] * 65535 + this.pixel[1] * 255 + this.pixel[2]) / 0xffffff;

    if (this.pixel[3] === 0) {
        hex = -hex;
    }

    cameraDepth = -hex * camera.far; // 相機坐標系中鼠標所在點的深度(注意:相機坐標系中的深度值為負值)
}

 

3. 根據鼠標在屏幕上的位置和相機空間深度,插值反算交點世界坐標系中的坐標。

let nearPosition = new THREE.Vector3(); // 鼠標屏幕位置在near處的相機坐標系中的坐標
let farPosition = new THREE.Vector3(); // 鼠標屏幕位置在far處的相機坐標系中的坐標
let world = new THREE.Vector3(); // 通過插值計算世界坐標

// 設備坐標
const deviceX = this.offsetX / width * 2 - 1;
const deviceY = - this.offsetY / height * 2 + 1;

// 近點
nearPosition.set(deviceX, deviceY, 1); // 屏幕坐標系:(0, 0, 1)
nearPosition.applyMatrix4(camera.projectionMatrixInverse); // 相機坐標系:(0, 0, -far)

// 遠點
farPosition.set(deviceX, deviceY, -1); // 屏幕坐標系:(0, 0, -1)
farPosition.applyMatrix4(camera.projectionMatrixInverse); // 相機坐標系:(0, 0, -near)

// 在相機空間,根據深度,按比例計算出相機空間x和y值。
const t = (cameraDepth - nearPosition.z) / (farPosition.z - nearPosition.z);

// 將交點從相機空間中的坐標,轉換到世界坐標系坐標。
world.set(
    nearPosition.x + (farPosition.x - nearPosition.x) * t,
    nearPosition.y + (farPosition.y - nearPosition.y) * t,
    cameraDepth
);
world.applyMatrix4(camera.matrixWorld);

 

完整代碼:

相關應用

使用gpu選取物體並計算交點位置,多用於需要性能非常高的情況。例如:

1. 鼠標移動到三維模型上的hover效果。

2. 添加模型時,模型隨着鼠標移動,實時預覽模型放到場景中的效果。

3. 距離測量、面積測量等工具,線條和多邊形隨着鼠標在平面上移動,實時預覽效果,並計算長度和面積。

4. 場景和模型非常大,光線投射法選取速度很慢,用戶體驗非常不好。

這裏給一個使用gpu選取物體和實現鼠標hover效果的圖片。紅色邊框是選取效果,黃色半透明效果是鼠標hover效果。

 

 

 

看不明白?可能你不太熟悉three.js中的各種投影運算。下面給出three.js中的投影運算公式。

 

three.js中的投影運算

1. modelViewMatrix = camera.matrixWorldInverse * object.matrixWorld

2. viewMatrix = camera.matrixWorldInverse

3. modelMatrix = object.matrixWorld

4. project = applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix )

5. unproject = applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld )

6. gl_Position = projectionMatrix * modelViewMatrix * position
                      = projectionMatrix * camera.matrixWorldInverse * matrixWorld * position
                      = projectionMatrix * viewMatrix * modelMatrix * position

 

參考資料:

1. 完整實現代碼:

2. 基於three.js的開源三維場景編輯器:

3. OpenGL中使用着色器繪製深度值:https://stackoverflow.com/questions/6408851/draw-the-depth-value-in-opengl-using-shaders

4. 在glsl中,獲取真實的片元着色器深度值:https://gamedev.stackexchange.com/questions/93055/getting-the-real-fragment-depth-in-glsl

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益